Skip to content

fix: handle None return from error handler in no-output background callbacks - #4012

Open
dajiaohuang wants to merge 9 commits into
plotly:devfrom
dajiaohuang:fix/3628-background-callback-error-handler
Open

dajiaohuang wants to merge 9 commits into
plotly:devfrom
dajiaohuang:fix/3628-background-callback-error-handler

Conversation

@dajiaohuang

Copy link
Copy Markdown
Contributor

Summary

When a background callback with no outputs raises an exception, the error handler is called. If the error handler returns None, the code was incorrectly setting output_value = NoUpdate(), which then triggered an InvalidCallbackReturnValue exception at the output validation step.

Fix

This change adds the same output_spec check that exists in the non-background callback path to the background callback path at _callback.py:635, ensuring that NoUpdate() is only set when there are actual outputs to update.

Testing

The issue includes a minimal reproducible example that can be used to verify the fix.

Related Issue

Fixes #3628

…llbacks

When a background callback with no outputs raises an exception, the error
handler is called. If the error handler returns None, the code was
incorrectly setting output_value = NoUpdate(), which then triggered an
InvalidCallbackReturnValue exception at the output validation step.

This change adds the same output_spec check that exists in the non-background
callback path (line 866) to the background callback path (line 635), ensuring
that NoUpdate() is only set when there are actual outputs to update.

Fixes plotly#3628
@camdecoster

Copy link
Copy Markdown
Contributor

Thanks for the PR! Could you please look into the test failures and see what's going on?

@T4rk1n T4rk1n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works good, and the lcbc019 test covers it. One gap: the job-cancel branch a few lines above has the same NoUpdate() for no-output callbacks, so cancelling a no-output background callback still raises InvalidCallbackReturnValue. Can you fix that here too, since it's the same bug? Please also add a CHANGELOG entry under Fixed for #3628.

Comment thread dash/_callback.py
Comment thread tests/unit/mcp/tools/test_mcp_background_callbacks.py Outdated
@dajiaohuang

Copy link
Copy Markdown
Contributor Author

感谢你的反馈!你说得对,测试确实过度使用了 mock,我会简化测试方法。

- The cancelled-job branch in _handle_rest_background_callback now sets
  NoUpdate() only when output_spec is truthy (callbacks with outputs),
  matching the error-handler guard added earlier. Without outputs,
  output_value stays None so _prepare_response does not raise
  InvalidCallbackReturnValue.
- Replace the heavily-mocked test_mcpbg012 with two integration-style
  tests that register a real no-output background callback via
  @app.callback and exercise both the completed and cancelled paths
  through the actual MCP tasks/result endpoint.
- Add CHANGELOG entry for plotly#3628 / plotly#4012.
@dajiaohuang

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback:

  1. Cancel branch fix: The cancelled-job branch in _handle_rest_background_callback (dash/_callback.py:689) now applies the same output_spec guard that was added for the error-handler branch — it sets NoUpdate() only when output_spec is truthy (i.e., the callback has outputs), and leaves output_value = None for no-output callbacks. This prevents InvalidCallbackReturnValue from being raised when a no-output background callback is cancelled via cancel= or when its job is gone on poll.

  2. Test simplification: Dropped the heavily-mocked test_mcpbg012_tasks_result_passes_output_spec (which monkeypatched 6 internals and copied the whole signature) and replaced it with two integration-style tests that register a real no-output background callback via @app.callback and exercise the actual MCP dispatch path:

    • test_mcpbg012_no_output_task_result_does_not_raise — polls tasks/result after a no-output callback completes successfully.
    • test_mcpbg013_cancel_no_output_task_does_not_raise — calls tasks/cancel and then tasks/result on a no-output task to cover the cancel branch.
  3. CHANGELOG: Added a Fixed entry under Unreleased referencing fix: handle None return from error handler in no-output background callbacks #4012 / [BUG] Exceptions in Background Callback Cause InvalidCallbackReturnValue When Using Custom Error Handler #3628.

…llback-error-handler

# Conflicts:
#	CHANGELOG.md
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Exceptions in Background Callback Cause InvalidCallbackReturnValue When Using Custom Error Handler

3 participants