Skip to content

fix: consistent error handling between run_sync and run_stream for invalid output tool calls#4860

Open
Spectual wants to merge 3 commits intopydantic:mainfrom
Spectual:fix/streaming-output-validation
Open

fix: consistent error handling between run_sync and run_stream for invalid output tool calls#4860
Spectual wants to merge 3 commits intopydantic:mainfrom
Spectual:fix/streaming-output-validation

Conversation

@Spectual
Copy link
Copy Markdown

Summary

Fix inconsistent behavior between agent.run_sync and agent.run_stream when no valid output tool calls are returned by the model.

Problem

As reported in #3638, when the model returns no valid output tool calls:

  • agent.run_sync raises UnexpectedModelBehavior
  • agent.run_stream + get_output() raises pydantic_core.ValidationError

This inconsistency makes it difficult to write reliable error handling code.

Solution

Updated the streaming output validation in result.py to catch ValidationError during output processing and convert it to UnexpectedModelBehavior, matching the behavior of the non-streaming path.

Testing

  • Added test cases verifying consistent error behavior between streaming and non-streaming paths
  • All existing tests continue to pass

Fixes #3638

…valid output tool calls

Fixes pydantic#3638

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added size: S Small PR (≤100 weighted lines) bug Report that something isn't working, or PR implementing a fix labels Mar 26, 2026
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 4 potential issues.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +212 to +215
except (ValidationError, exceptions.ToolRetryError) as e:
if not allow_partial:
raise exceptions.UnexpectedModelBehavior('Invalid output') from e
raise
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Mar 26, 2026

Choose a reason for hiding this comment

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

🚩 ModelRetry not caught by the new except block

The new except at pydantic_ai_slim/pydantic_ai/result.py:245 catches ValidationError and ToolRetryError but not ModelRetry. ModelRetry can propagate from handle_call (via validate_tool_call with wrap_validation_errors=False at pydantic_ai_slim/pydantic_ai/_tool_manager.py:393-394) when a tool's validation hooks raise ModelRetry, or from the text processor path (via execute_traced_output_function at pydantic_ai_slim/pydantic_ai/_output.py:144-145). If ModelRetry is raised during final validation (allow_partial=False), it would leak to the user unwrapped — the same class of issue this PR fixes for ValidationError. However, this is pre-existing behavior unchanged by this PR, so it's an incomplete fix rather than a regression. In practice, ModelRetry from output tool validation is uncommon in the streaming context.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

devin-ai-integration[bot]

This comment was marked as resolved.

@Spectual Spectual force-pushed the fix/streaming-output-validation branch from 4400d63 to 37edf30 Compare March 27, 2026 00:11
…e_output

Merge the two identical `except (ValidationError, ToolRetryError)` blocks
into a single try/except wrapping the entire if/elif chain, per AGENTS.md
rule 894. The intermediate branches (deferred_tool_requests, allows_image)
do not raise these exceptions so the consolidation is safe.
@github-actions github-actions bot added size: M Medium PR (101-500 weighted lines) and removed size: S Small PR (≤100 weighted lines) labels Mar 27, 2026
@Spectual Spectual force-pushed the fix/streaming-output-validation branch from 2cef5c1 to 98bba49 Compare March 27, 2026 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Report that something isn't working, or PR implementing a fix size: M Medium PR (101-500 weighted lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent behavior between agent.run_sync and agent.run_stream when no output tool calls are valid

1 participant