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.

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.