plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.53k stars 2.07k forks source link

Typo in [running] in background callbacks breaks the callback and shows no error message #2970

Open e-wallace opened 2 months ago

e-wallace commented 2 months ago

Describe your context In the callback definition if there's a typo in the running section, the callback will break but no errors will show.

@callback( output=Output("paragraph_id", "children"), inputs=Input("button_id", "n_clicks"), background=True, running=[ (Output("button-id", "disabled"), True, False), ], ) Describe the bug

You are able to click the button, but it doesn't change colour and go offline. Nothing happens and no errors show.

Logs say the callback succeeded

[2024-08-28 16:37:31,801: INFO/ForkPoolWorker-3] Task long_callback_30e712a6918f1cf3326b54541e42a0453585264e[402c37d9-0d28-46bf-827e-8a8da7c03402] succeeded in 2.0058521699975245s: None

You can reproduce the error with the following code:

app.py

import time
import os

from dash import Dash, DiskcacheManager, CeleryManager, Input, Output, html, callback

if 'REDIS_URL' in os.environ:
    # Use Redis & Celery if REDIS_URL set as an env variable
    from celery import Celery
    celery_app = Celery(__name__, broker=os.environ['REDIS_URL'], backend=os.environ['REDIS_URL'])
    background_callback_manager = CeleryManager(celery_app)

else:
    # Diskcache for non-production apps when developing locally
    import diskcache
    cache = diskcache.Cache("./cache")
    background_callback_manager = DiskcacheManager(cache)

app = Dash(__name__, background_callback_manager=background_callback_manager)

app.layout = html.Div(
    [
        html.Div([html.P(id="paragraph_id", children=["Button not clicked"])]),
        html.Button(id="button_id", children="Run Job!"),
    ]
)

@callback(
    output=Output("paragraph_id", "children"),
    inputs=Input("button_id", "n_clicks"),
    background=True,
    running=[
        (Output("button-id", "disabled"), True, False),
    ],
)
def update_clicks(n_clicks):
    time.sleep(2.0)
    return [f"Clicked {n_clicks} times"]

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

procfile

web: gunicorn app:server --workers 4
celery -A app:celery_app worker --loglevel=INFO --concurrency=2

Expected behavior

You expect to see an error message like "The output ID does not match any of the IDs in the layout".

Screenshots

Here is the error that shows in the browser console logs

Screen Shot 2024-08-28 at 12 32 08 PM
aGitForEveryone commented 2 months ago

This bug is already solved for the upcoming release. See issue #2897 and PR #2898. The issue came up for me, because I wanted to allow callbacks with components in running that were not yet in the layout. The future behavior will be that if surpress_callback_exceptions=True then no warning is given, and if it is disabled, you will see an error that the component doesn't exist in the layout.

aGitForEveryone commented 2 months ago

New version is live now. Update to 2.18.0 to get the correct error handling.