What are some good options for showing some kind of error message to the user when a callback fails (throws an exception)? Right now there is the traceback in the console, but looking at the webpage, all that the user sees is that nothing happened.
I’ve thought through some ways that this could be handled explicitly through Dash (a div with error messages that is the output of the potentially failing callback) but it seems very messy (especially if multiple callbacks involved) and involves duplicating information. Does react itself have some useful framework that could be used here?
Yep, the thread you linked to is just about improving the user experience for the developer, not the user of the app. I’m actually not sure that there is an easy way to achieve this at the moment, although @chriddyp might have some ideas.
From my understanding, callbacks wire up successful responses only to where they were originally being targeted. So there’s no current way to route a callback’s output to a different element midway through handling the callback. And routing it to a different element is going to be necessary for error messages since the target property isn’t always a suitable destination for an error message, or as you mention, might be an input of a chained callback.
I’ll second that this would be useful, to have a way to inform user of errors during execution of callback.
Another frustration of mine is when a Javascript error happens, the UI just stops being responsive with no feedback to user. As a developer i can know this happened by looking at chrome->developer tools. but user will have no idea what to do --> when the solution is that user should be informed of error and told to reload the page.
@app.handle_callback_errors(Output('my-user-warning-div', 'value'), [my_callback_func])
def handle_error(error):
# whenever a callback (e.g. my_callback_func) raises a PreventUpdate exception, the exception
# is passed to this handle_error function, which is itself a callback and can display a warning to the user
Would something like this be possible within the design of Dash?
Or even something like an Error class in addition to State, Input, Output, to explicitly handle errors when attempting to update a specified component. And then you could write a callback ‘as normal’ but with the appropriate Error as an input instead.
e.g. something like
@app.callback(Output('my-user-warning-div', 'value'), [Error('component-I-wanted-to-update', 'value-I-wanted-to-update')]:
def handle_error(error):
# error is passed from the callback that failed when trying to update `component-I-wanted-to-update`
One pattern is to update the children property of a parent div instead of the attribute of your actual component. Then, your callback looks like:
@app.callback(Output('container', 'children'), [...])
def update_output(...)
try:
# ...
return dcc.Graph(...) # or any other component
except:
return html.Div('An error occurred') # full control over the error UI
Thanks – that would definitely work, and is an improvement on how I’m currently doing things.
There are still two issues with that approach however:
If you want to keep the existing dcc.Graph, i.e. the update fails, but the existing graph should still be displayed, whereas if you overwrite this container div with an error message it is not (I’m sure there’s a way to work around this though)
Ideally, if the error is triggered by a control, I’d like to display an error specifically alongside that control – imagine an app where you have a single master graph, but a dozen separate controls that can do things to that graph, some of which (might) fail
Neither of these are show-stoppers, I’m just wondering if there’s a better way to do this.
The first one is easy enough – just pass the current graph as a State variable.
The second one I would also like to do but seems extremely messy in the current framework. I guess you could modify the position of the output div, but you’d still not be sure of which control was triggered.
i switched to creating all my callbacks programmatically, so i handle all errors in the function that creates the callback
this is useful mostly for logging the errors (i found that exceptions in callbacks fail silently otherwise ,due to that suppress exceptions flags in a multi page app)
if there is a way of firing an event from within a callback and by that updating another output component (not the one used in the callback definition, that would solve the issue of displaying the error to the end user
@ pepe.bari - You still update the figure via the return statement. Including the graph as State simply makes the (current) state, i.e. how the graph looked before the callback invoked, available in the callback context. Using this state as the return value, the graph will stay unchanged.
Please elaborate. Is ‘container’ the id of an html.Div in the layout? But I thought that returning a dcc.Graph requires the other variable to be 'figure’ and not 'children'. I’m pretty sure I tried returning a dcc.Graph to an html.Div as 'children' and it did not render properly.
I’ve also noticed that I get many more error messages in JupyterDash where as in my Flask app with embedded Dash apps, they only failed silently.
Since Dash 2.17.0 I think this is possible with set_props.
I attach a decorator to Dash.callback that catches any exceptions (except PreventUpdate) and uses set_props to show the user error info (a dbc Modal in my case). I think it’s needed for set_props to work to return dash.no_update rather than re-raise the error. To return the right number of dash.no_update’s I do some python argument processing:
def error_handle_callback(outputs: int):
def decorator(func):
def wrapper(*args, **error_handle_kwargs):
try:
result = func(*args, **error_handle_kwargs)
except Exception as e:
if isinstance(e, PreventUpdate):
raise PreventUpdate
set_props('error-modal', {'is_open': True})
error_traceback = exception_str()
set_props('callback-error', {'children': error_traceback})
return tuple(dash.no_update for _ in range(outputs))
return result
return wrapper
return decorator
callback = dash_app.callback
def new_callback(*args, **decorator_kwargs):
def decorator(func):
new_func = error_handle_callback(outputs=sum([isinstance(arg, Output) for arg in args]))(func)
return callback(*args, **decorator_kwargs)(new_func)
return decorator
dash_app.callback = new_callback
# ensure to create all callbacks after this point