Skip to content

fix(streaming): add direct type-map fallback for raw dict events (fixes #941)#1448

Open
Zelys-DFKH wants to merge 1 commit intoanthropics:mainfrom
Zelys-DFKH:fix/content-block-delta-deserialization
Open

fix(streaming): add direct type-map fallback for raw dict events (fixes #941)#1448
Zelys-DFKH wants to merge 1 commit intoanthropics:mainfrom
Zelys-DFKH:fix/content-block-delta-deserialization

Conversation

@Zelys-DFKH
Copy link
Copy Markdown

@Zelys-DFKH Zelys-DFKH commented Apr 25, 2026

Problem

The accumulate_event function in the streaming message parser fails with:

Unexpected event runtime type, after deserialising twice - {...} - <class 'dict'>

This occurs when SSE events like content_block_delta are received as raw dicts instead of properly deserialized Pydantic models.

Root Cause

Pydantic's union discriminator can fail silently in certain versions or configurations. Instead of raising an error, construct_type_unchecked falls back to returning the raw dict—making the failure invisible until the type check fails later.

Solution

Add a fallback type-map lookup when the dict is returned. This approach:

  1. Defines _RAW_EVENT_TYPE_MAP and _BETA_RAW_EVENT_TYPE_MAP dictionaries that map event type strings to their corresponding Pydantic model classes
  2. When a dict is returned, uses the dict's type field to look up the correct event class
  3. Constructs the model directly via model_construct(**raw_dict)

The fallback catches the silent failure early. Well-formed events now construct correctly, and malformed events still raise the original error—so we get both robustness and clear error messages.

Changes

  • Added imports for all RawMessageStreamEvent union members to both _messages.py and _beta_messages.py
  • Added _RAW_EVENT_TYPE_MAP and _BETA_RAW_EVENT_TYPE_MAP dictionaries
  • Updated error handling in accumulate_event to implement the fallback before raising an error

Testing

This fix has been tested to handle:

  • content_block_delta events arriving as raw dicts
  • Proper fallback to existing error when dict is malformed or type is unknown
  • Backward compatibility with existing working implementations

Fixes anthropics#941 — `content_block_delta` SSE events fail to deserialize with
"Unexpected event runtime type, after deserialising twice" in some
environments where Pydantic's discriminator resolution silently returns
the raw dict instead of raising an error or constructing the proper model.

Root Cause:
The union discriminator deserialization in older Pydantic versions can
silently fall back to returning the raw dict when the discriminator
lookup fails, rather than raising an explicit error.

Solution:
Add a fallback type-map lookup when `construct_type_unchecked` returns
a dict instead of a BaseModel. Use the 'type' field from the dict to
look up the correct event class and construct it directly via
`model_construct`.

Changes:
- Added event type imports to _messages.py and _beta_messages.py
- Added _RAW_EVENT_TYPE_MAP and _BETA_RAW_EVENT_TYPE_MAP dictionaries
- Updated error handling in accumulate_event to use the fallback
  when discriminator deserialization returns a dict

This fix ensures that well-formed events (including content_block_delta)
are always promoted to the correct BaseModel, even in environments where
the union discriminator mechanism fails silently.

Co-Authored-By: Zelys-DFKH <admin@dfkhelper.com>
@Zelys-DFKH Zelys-DFKH requested a review from a team as a code owner April 25, 2026 22:35
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.

1 participant