Skip to content

fix(react): leave list[str] items uncoerced when a schema is provided#953

Draft
cotovanu-cristian wants to merge 1 commit into
mainfrom
fix/coerce-liststr-items
Draft

fix(react): leave list[str] items uncoerced when a schema is provided#953
cotovanu-cristian wants to merge 1 commit into
mainfrom
fix/coerce-liststr-items

Conversation

@cotovanu-cristian

Copy link
Copy Markdown
Collaborator

Summary

When coerce_json_strings is given a schema, it protects scalar str-typed fields from coercion
(if annotation is str: return value). But _coerce_field's list branch never extended that
protection to list[str] item types: each element was passed to blind coercion, so a list[str]
element that merely looks like JSON ('{"a": 1}', '[1, 2]') was silently parsed into a
dict/list — against the declared str element type.

The contract

coerce_json_strings' docstring: "When a schema is provided, str-typed fields are left
untouched."
The elements of a list[str] field are str-typed per the schema and must be left
untouched, exactly like a scalar str field. The scalar-field form of this rule is already locked
by test_str_field_preserved_dict_field_coerced.

Root cause & fix

agent/react/json_utils.py::_coerce_field — the list branch now mirrors the scalar guard: when the
(optional-unwrapped) item type is str, the list elements are returned untouched. Model-typed and
unknown item types are unchanged (model items still recurse with their child schema). One guard in
the shared producer every coerce_json_strings(dict, schema) field routes through — not a
per-caller patch.

Tests

Added TestCoerceListOfStrProtected: list[str] items not coerced, Optional[list[str]] items
not coerced, and a regression guard that model-typed list items are still coerced. The first
two fail on current code and pass with the fix; the full json_utils suite passes.

coerce_json_strings protects scalar str-typed fields from coercion when a
schema is supplied, but the list branch of _coerce_field never extended that
protection to list[str] item types: each element was passed to blind
coercion, so a string element that merely looks like JSON ('{"a": 1}',
'[1, 2]') was silently parsed into a dict/list against the declared str type.

Guard the list branch the same way the scalar branch is guarded: when the
(optional-unwrapped) item type is str, leave the list elements untouched.
Model-typed and unknown item types are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@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.

1 participant