Duplicate Callback Outputs

A component/property pair can only be the Output of one callback. A component property pair means the id of the component and the property. In this html.H1 component, it’s 'app-heading', 'children':

html.H1(children='Analytics app', id='app-heading')

If you add 'app-heading', 'children' as an Output on two callbacks in your app you’ll get a “Duplicate callback outputs” error when running in debug mode.

<img>

Duplicate Callback Outputs Example

This is a complete example demonstrating an app that throws a “Duplicate callback outputs” error. Here we have two html.Button components and a dcc.Graph component in our app.layout:

app.layout = html.Div([
    html.Button('Draw Graph', id='draw'),
    html.Button('Reset Graph', id='reset'),
    dcc.Graph(id='our-graph')
])

If we try to update the graph component’s ('our-graph') 'fig' property differently depending on which button is clicked, we cannot do this by adding 'our-graph', 'fig' as an Output on two callbacks:

@app.callback(
    Output('our-graph', 'figure'),
    Input('draw', 'n_clicks'),
    prevent_initial_call=True
)
def draw_graph(_):
    df = px.data.iris()
    return px.scatter(df, x=df.columns[0], y=df.columns[1])

@app.callback(
    Output('our-graph', 'figure'),
    Input('reset', 'n_clicks'),
    prevent_initial_call=True
)
def reset_graph(_):
    return go.Figure()

Updating the Same Output From Different Inputs

We can update our graph in the above example differently based on the inputs by combining the inputs into one callback and using dash.callback_context to determine which input triggered the callback.

Here is the same example rewritten. Our new callback has the two inputs. It gets the ID of the component that triggered the callback using ctx.triggered_id. This will return either draw or reset. If it is draw, the callback returns draw_graph(). If it is reset it returns reset_graph():

from dash import Dash, Input, Output, ctx, html, dcc
import plotly.express as px
import plotly.graph_objects as go

app = Dash(__name__)

app.layout = html.Div([
    html.Button('Draw Graph', id='draw'),
    html.Button('Reset Graph', id='reset'),
    dcc.Graph(id='graph')
])

@app.callback(
    Output('graph', 'figure'),
    Input('reset', 'n_clicks'),
    Input('draw', 'n_clicks'),
    prevent_initial_call=True
)
def update_graph(b1, b2):
    triggered_id = ctx.triggered_id
    print(triggered_id)
    if triggered_id == 'reset':
         return reset_graph()
    elif triggered_id == 'draw':
         return draw_graph()

def draw_graph():
    df = px.data.iris()
    return px.scatter(df, x=df.columns[0], y=df.columns[1])

def reset_graph():
    return go.Figure()

app.run_server(debug=True)

See Determining Which Callback Input Changed for more on dash.callback_context and understanding which input triggered a callback.