Skip to content

Conversation

@Ashutosh0x
Copy link

This PR fixes a bug where construct_type failed to correctly unwrap Annotated types when they were wrapped in TypeAlias (common in newer Python versions/setups) or when metadata was explicitly provided. This ensures that discriminated unions like RawMessageStreamEvent are correctly processed, preventing intermittent Unexpected event runtime type errors during streaming (fixes #941).

…ctly

Fixes intermittent deserialization failures where Annotated types were not unwrapped if wrapped in a TypeAlias or ifmetadata was provided. This ensures discriminated unions like RawMessageStreamEvent are correctly processed.
@Ashutosh0x Ashutosh0x requested a review from a team as a code owner February 7, 2026 16:09
@Ashutosh0x
Copy link
Author

I've verified the fix locally with reproduction scripts specifically targeting the TypeAliasType + Annotated nesting scenario. This should resolve the reported intermittent issues with RawMessageStreamEvent deserialization.

@felixfbecker
Copy link
Contributor

felixfbecker commented Feb 7, 2026

Thank you for your contribution! Would it be possible to add a test for this?

…discriminated unions and fix Pydantic v1 model_dump regression
@Ashutosh0x
Copy link
Author

@felixfbecker I've added comprehensive regression tests to tests/test_models.py covering several nested TypeAliasType and Annotated scenarios. These verify that the fix correctly unwraps the types and collects the necessary metadata for discriminated unions like RawMessageStreamEvent. I also fixed a minor regression in the model_dump shim that was causing issues on older Python versions (and Pydantic v1). Ready for another look! 🚀

@Ashutosh0x
Copy link
Author

@felixfbecker I've refined the fix to address the failure observed in CI. The issue was that passing a TypeAliasType directly to Pydantic's validate_type (which uses TypeAdapter) was inconsistent across Python versions/Pydantic environments, sometimes returning a raw dict instead of co-ercing to a model instance.

I've updated construct_type to:

  1. Fully unwrap the type before passing it to validate_type. This ensures Pydantic sees a standard Union (or model class) and correctly identifies if the data is structurally valid.
  2. Collect all metadata from all Annotated layers, ensuring discriminators are never missed.
  3. Fall back reliably to the manual construction logic if validate_type returns a dict for a union that has a discriminator.

This improved approach is more robust and passes correctly in environments where TypeAliasType might be handled differently by Pydantic.

@Ashutosh0x
Copy link
Author

CI Passing ✅

All CI checks are now passing after the latest commits. Here's a summary of the changes made:

Changes Summary

src/anthropic/_models.py:

  • Refactored construct_type to use a local variable _type for the unwrapped type, ensuring issubclass() is only called on actual classes
  • Removed unnecessary cast(Any, ...) calls that pyright flagged as redundant
  • Added # pyright: ignore[reportUnknownVariableType] comments on lines where dynamic method calls on Any types return unknown types (lines 531, 579, 585)

tests/test_models.py:

  • Moved all TypeAliasType definitions to module level to satisfy pyright's requirement that type aliases be assigned to variables with matching names
  • Added regression tests for nested TypeAliasType and Annotated unwrapping

Test Coverage

All 51 tests pass, including the three new regression tests:

  • test_type_alias_annotated_unwrapping - TypeAliasType of Annotated Union
  • test_annotated_type_alias_unwrapping - Annotated of TypeAliasType of Union
  • test_deeply_nested_unwrapping - Deeply nested TypeAliasType and Annotated

Ready for review!

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.

content_block_delta event not deserialized correctly during streaming

2 participants