Skip to content

[BUG] Long Callbacks: Raising Error Causes Infinite Loop (Including PreventUpdate) #1821

@Robbie-Palmer

Description

@Robbie-Palmer

Environment

python               3.9.7
dash                 2.0.0

Describe the bug

If an error is raised within a long callback, an infinite loop of re-raising this error is triggered, as a new process tries again and again
This is triggered by the _long_callback_interval_1.n_intervals property

Expected behavior

The error would be handled like with normal callbacks, and the long callback wouldn't be called by new processes

Minimum Reproducible Example

import time

import dash
import diskcache
from dash import html
from dash.dependencies import Input, Output
from dash.long_callback import DiskcacheLongCallbackManager

cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)

app = dash.Dash(__name__, long_callback_manager=long_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!"),
    ]
)


@app.long_callback(
    output=Output("paragraph_id", "children"),
    inputs=Input("button_id", "n_clicks"),
    running=[
        (Output("button_id", "disabled"), True, False),
    ],
)
def callback(n_clicks):
    if n_clicks == 2:
        raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
    time.sleep(2.0)
    return [f"Clicked {n_clicks} times"]


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

In the above example, can safely click the button once, then on the second click raising PreventUpdate causes the infinite loop
With logged messages of:

Process Process-3:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: button_id.n_clicks
Process Process-4:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: _long_callback_interval_1.n_intervals
Process Process-5:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: _long_callback_interval_1.n_intervals
Process Process-6:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: _long_callback_interval_1.n_intervals
Process Process-7:
...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions