Callback Output Already in Use Error on Page Refresh in Dash App

I’m experiencing an issue with my Dash app where I encounter the error:
“In the callback for output(s):
selected-shade-data.data
Output 0 (selected-shade-data.data) is already in use.”
This error occurs whenever I refresh the page. I’ve ensured that my callbacks are registered only once and that the layout loading logic should not be causing multiple callback registrations. Despite these precautions, the error persists.

Here’s a brief overview of my setup:

  • My app uses Flask for server-side routing and authentication.
  • The layout is dynamically set based on the user ID from the URL route.
  • Callbacks are registered outside the layout loading logic to ensure they are only registered once.
  • I have set suppress_callback_exceptions=True in my app initialization.

Below is a simplified version of my code:

from utils.connections import get_database_data
from utils.layouts import get_main_layout
#other dependencies

external_stylesheets = [
    dbc.themes.LUX,
    "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css",
]

server = Flask(__name__)
server.secret_key = 'secret_key'
app = dash.Dash(__name__, server=server, external_stylesheets=external_stylesheets, suppress_callback_exceptions=True)
       
@app.server.route('/<user_id>')
def index(user_id):
    username = get_username_from_user_id(user_id)
    if username:
        data, all_data, names = get_database_data(username)
        app.layout = get_main_layout(app, username)
        @app.callback(
            Output("selected-shade-data", "data"), [Input("cie-2000-each-shades", "clickData")]
        )
        def update_selected_shade_data(clickData):
            if clickData is None:
                return {}

            # Récupérer les données du point cliqué
            point_data = clickData["points"][0]["customdata"]

            # Extraction des données
            (
                shade1_name,
                shade1_rgb,
                shade1_undertone,
                deltaE,
                shade2_name,
                shade2_rgb,
                shade2_undertone,
            ) = point_data
            shade1 = {
                "shade_name": shade1_name,
                "rgb": shade1_rgb,
                "undertone": shade1_undertone,
            }
            shade2 = {
                "shade_name": shade2_name,
                "rgb": shade2_rgb,
                "undertone": shade2_undertone,
            }

            return {"shade1": shade1, "deltaE": deltaE, "shade2": shade2}
        return app.index()
    else:
        return "User not found", 404

if __name__ == '__main__':
    app.run_server(host='0.0.0.0', port=8050, debug=True)

here is my layouts.py:

from dash import html
import dash_bootstrap_components as dbc
from dash import dcc
from utils.connections import get_database_data
def get_main_layout(app, username):
    data, all_data, names = get_database_data(username)
    layout = html.Div(
        id='app-content',
        style={
            "backgroundColor": "#0C0C0C",
            "color": "white",
            "padding-left": "15vh",
            "padding-right": "15vh",
            "padding-bottom": "15vh",
        },
        children=[
            dbc.Row(
                [
                    dbc.Col(
                        [
                            html.Div(
                                                        id="part-4-plot-22",
                                                        style={"padding": "10px"},
                                                        children=[
                                                            html.H3(
                                                                "Depth of shade range between each shade",
                                                                style={
                                                                    "color": "white",
                                                                    "padding-top": "10px",
                                                                    "padding-bottom": "10px",
                                                                },
                                                            ),
                                                            html.Div(
                                                                id="part-4-plot-2-description",
                                                                style={
                                                                    "padding": "10px"
                                                                },
                                                                children=[
                                                                    html.H5(
                                                                        [
                                                                            html.I(
                                                                                className="fas fa-caret-right",
                                                                                id="part-4-plot-2-trigger-icon",
                                                                            ),
                                                                            " Comprendre la profondeur entre 2 teintes voisines",
                                                                        ],
                                                                        id="part-4-plot-2-trigger",
                                                                        style={
                                                                            "cursor": "pointer"
                                                                        },
                                                                    ),
                                                                    dcc.Interval(
                                                                        id="part-4-plot-2-dummy-interval",
                                                                        interval=60
                                                                        * 60
                                                                        * 1000,  # en millisecondes, donc ici 60 minutes
                                                                        max_intervals=1,  # ce composant déclenchera le callback une seule fois
                                                                    ),
                                                                    dbc.Collapse(
                                                                        html.Div(
                                                                            id="part-4-plot-2-content",
                                                                            children="",
                                                                        ),
                                                                        id="collapse-part-4-plot-2",
                                                                        is_open=False,
                                                                    ),
                                                                ],
                                                            ),
                                                            dcc.Store(
                                                                id="selected-shade-data",
                                                                data={},
                                                            ),
                                                            html.Div(
                                                                id="part-4-plot-2",
                                                                children=[
                                                                    dcc.Loading(
                                                                        id="loading-part-4-plot-2",
                                                                        type="circle",
                                                                        color="#1f77b4",
                                                                        children=[
                                                                            dcc.Graph(
                                                                                id="heatmap-cie-2000-each-shades",
                                                                            ),
                                                                        ],
                                                                    )
                                                                ],
                                                            ),
                                                            html.Div(id='description-container-10', style={'marginTop': '20px'}),
                                                            html.Br(),
                                                            html.Div(
                                                                id="shade-detail-display",
                                                                children=[],
                                                            ),
                                                            html.Div(
                                                                id="each-shade-detail-display",
                                                                children=[
                                                                    dcc.Loading(
                                                                        id="loading-each-shade-detail-display",
                                                                        type="circle",
                                                                        color="#1f77b4",
                                                                        children=[
                                                                            dcc.Graph(
                                                                                id="cie-2000-each-shades",
                                                                            ),
                                                                        ],
                                                                    )
                                                                ],
                                                            ),
                                                        ],
                                                    ),
                                                    #other components
                                            ])])])
return layout

You should move the callback definition out of the route function. With your current code, you register a new callback when the route function is invoked, i.e. on each page load.

Thank you @Emil for your direct and to-the-point answer. The main issue I’m facing is that I want to retrieve the user_id so I can use it outside of the route function, but I haven’t been able to find a way to achieve that.

Hello @helamri,

You can store the user in a session cookie, this is universally available by pulling the flask session in callbacks.

Or you can use a route like flask login, etc which also uses a session cookie, but is a full package that allows for protecting apps, etc.

With dash-auth, it is now also possible to specifically and granularly control what callbacks, layouts, etc specific users have access to.