Skip to content

fix(serialization): surface collected deserialization errors instead of masking them - #27

Open
spichen wants to merge 1 commit into
oi-forkfrom
fix/surface-deserialization-errors
Open

fix(serialization): surface collected deserialization errors instead of masking them#27
spichen wants to merge 1 commit into
oi-forkfrom
fix/surface-deserialization-errors

Conversation

@spichen

@spichen spichen commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Problem

PydanticComponentDeserializationPlugin.deserialize rebuilds the collected validation errors into pydantic_core.InitErrorDetails carrying only (type, loc):

InitErrorDetails(type=e.type, loc=e.loc, input=())

ValidationError.from_exception_data then re-derives each builtin type's required ctx (value_error needs {"error": <exc>}, gt needs gt, …). With none supplied, a collected value_error — any component model_validator that raises ValueError — makes from_exception_data itself raise:

TypeError: 'error' required in context

…which masks the real cause. Seen in the field as a Swarm-used-as-a-Flow-node AgentNode port mismatch (ValueError("... expected a property titled 'evidence_status' ...")) surfacing only as the opaque TypeError: ValueError: 'error' required in context.

Fix

Rebuild each collected error with a PydanticCustomError(e.type, e.msg), which carries the message directly — rendered verbatim (no ctx, no template interpolation). This reconstructs an error of any type without the per-type ctx dance, and preserves both the original type and msg.

Notably simpler than special-casing value_error/ctx: no branch on type, no message munging, no double-prefixing.

Tests

tests/serialization/test_deserialization_error_surfacing.py:

  • a collected value_error re-raises as a ValidationError (not TypeError) with its message verbatim (no added/doubled Value error, prefix);
  • the original error type is preserved (a missing stays missing, not flattened to value_error);
  • messages carrying literal {/} (JSON) survive verbatim (not treated as a format template);
  • multiple errors all surface, error_count == 2.

Full tests/serialization/ suite: 303 passed (the 4 pre-existing test_llm_node.py default_llm_config fixture errors are unrelated to this change).

…of masking them

PydanticComponentDeserializationPlugin.deserialize rebuilt the collected
validation errors into pydantic_core InitErrorDetails carrying only
(type, loc). from_exception_data then re-derives each builtin type's
required ctx (value_error needs {"error": <exc>}, gt needs gt, ...), so a
collected value_error -- any component model_validator that raises
ValueError -- made from_exception_data itself raise
TypeError("'error' required in context"), hiding the real cause. (Seen in
the field as a swarm-as-flow-node AgentNode port mismatch surfacing as the
opaque "TypeError: ValueError: 'error' required in context".)

Rebuild each collected error with a PydanticCustomError that carries its
message directly (rendered verbatim, no ctx and no template interpolation),
so any error type reconstructs without the per-type ctx dance and both the
original type and message are preserved.
@spichen
spichen force-pushed the fix/surface-deserialization-errors branch from 6f083af to 04a03da Compare July 13, 2026 19:16
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