Error "too much recursion" when using callbacks debug screen

Hi, I’ve build a prototype webpage to use the url search params as input for a slider, and vice-versa. So if the url changes, the change should update the slider, and if the slider changes, the url should be updated. I have a working version, see below. But if I click on a icon in the Dash debug screen, it gives me an error ‘too much recursion’. What can I do to get rid of this error?

from dash import Dash, dcc, html, Input, Output, State, ctx, Patch
from urllib.parse import parse_qs, urlparse
import logging

logging.basicConfig(level=logging.INFO)

app = Dash(__name__)

# Define year range
YEARS = list(range(1990, 2026))

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    dcc.Store(id='store', data={'year': 2000, 'my_value': None}),
    dcc.Slider(
        id='year-slider',
        min=min(YEARS),
        max=max(YEARS),
        step=1,
        value=2000,
        marks={str(year): str(year) for year in YEARS[::5]},
        tooltip={"placement": "bottom", "always_visible": True}
    ),
    html.Div(id='output'),
])

# Store <--> url
@app.callback(
    Output('url', 'search'),
    Output('store', 'data', allow_duplicate=True),
    Input('url', 'search'),
    Input('store', 'data'),
    prevent_initial_call=True
)
def store_url_and_initial_value(url_search, current_data):
    trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]

    if trigger_id == 'url':
        params = parse_qs(urlparse(url_search).query)
        year = params.get('year', [2000])[0]
        logging.info(f"Trigger id is url, {year}")
        value1 = f"?year={year}" # url

        current_data = Patch()
        current_data["year"] = year
        value2 = current_data

        return value1, value2

    elif trigger_id == 'store':
        year = current_data['year']
        logging.info(f"Trigger id is store, {year}")
        value1 = f"?year={year}" # url

        current_data = Patch()
        current_data["year"] = year
        value2 = current_data

        return value1, value2

    else:
        raise PreventUpdate

# Update slider <---> store
@app.callback(
    Output('year-slider', 'value'),
    Output('store', 'data', allow_duplicate=True),
    Input('year-slider', 'value'),
    Input('store', 'data'),
    prevent_initial_call=True
)
def update_url_from_slider(year_slider, current_data):
    logging.info(f"Slider <--> store {current_data}")
    trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]

    if trigger_id == 'year-slider':
        value1 = year_slider

        current_data = Patch()
        current_data["year"] = year_slider
        value2 = current_data

        return value1, value2

    elif trigger_id == 'store':
        value1 = int(current_data['year'])

        current_data = Patch()
        value2 = current_data

        return value1, value2

    else:
        raise PreventUpdate

# Display the store data
@app.callback(
    Output('output', 'children'),
    Input('store', 'data')
)
def display_stored_data(store_data):
    stored_data = store_data
    return html.Div([
        f"Current store: {stored_data}",
    ])

if __name__ == '__main__':
    app.run(debug=True)

Hello @rudi.broekhuizen,

Welcome to the community!

You’re entering a loop with what you are trying to do. You’ll have to find a way to make it so if the values are the same in the store and the pathname that there isn’t an update.

1 Like

Thanks for your reply. I’ve tried the example from the Dash website about advanced callbacks. There is an example how to use circular callbacks. When I try that code (code below), and click on a item in the callback graph, I shows the same error message I got in my setup.

Is this a bug in the callbacks graph screen?

In both webbrowsers:

Oops! The callback graph threw an error.
Check the error list for details.

In Firefox webbrowser:

too much recursion

In Chrome webbrowser:

Maximum call stack size exceeded
from dash import Dash, html, dcc, Input, Output, callback, ctx

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(
    [
        dcc.Slider(
            id="slider-circular", min=0, max=20,
            marks={i: str(i) for i in range(21)},
            value=3
        ),
        dcc.Input(
            id="input-circular", type="number", min=0, max=20, value=3
        ),
    ]
)
@callback(
    Output("input-circular", "value"),
    Output("slider-circular", "value"),
    Input("input-circular", "value"),
    Input("slider-circular", "value"),
)
def callback(input_value, slider_value):
    trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
    value = input_value if trigger_id == "input-circular" else slider_value
    return value, value

if __name__ == '__main__':
    app.run(debug=True)

This is a known bug: [BUG] RangeError: Maximum call stack size exceeded in callback graph with circular dependencies · Issue #1656 · plotly/dash · GitHub.