Skip to content

coerce-input/validate: model stringified list arg reaches bundle as str (pydantic list_type) #372

@mgoldsborough

Description

@mgoldsborough

Summary

A bundle tool whose argument is typed Optional[list[...]] (FastMCP/pydantic → JSON Schema anyOf: [{type: array}, {type: null}]) intermittently fails when the model emits that argument as a JSON-encoded string ("[{...}]") instead of a real array. The bundle's pydantic rejects it:

1 validation error for call[<tool>]
<arg>
  Input should be a valid list [type=list_type, input_value='[{...}]', input_type=str]

Observed rate in one long document-editing session: ~2% of calls to a list-arg tool (self-recovered on retry).

Why this is surprising

The platform already has src/tools/coerce-input.ts, which is supposed to recover exactly this misencoding: for a property whose effective schema is array/object, it JSON.parses a string value before validation. The union-resolution path (effectiveSchemaFor) collapses anyOf: [array, null] to the array branch, so an Optional[list] arg should be coerced. And engine.ts runs coerceInputForSchema + validateToolInput before dispatch.

Yet the error that surfaced was the bundle's pydantic error, not the platform's Invalid tool input: — meaning the platform accepted the string and forwarded it. So either:

  1. the model's stringified value was malformed JSON (e.g. embedded content with unescaped quotes/braces), so tryJsonParse returned undefined and coercion left the string — and platform validation then also passed it through (needs confirming how validateToolInput treats a string against anyOf:[array,null]), or
  2. the platform's view of the tool's input schema for this arg differs from what the bundle enforces (schema-shape mismatch), so neither coercion nor validation engaged.

Repro needed

The exact pre-coercion argument is not recoverable from event logs (the value is truncated in the pydantic error and the raw arg isn't persisted). To fix at the right layer, reproduce with the full stringified argument and confirm which of (1)/(2) applies:

  • If malformed JSON: decide whether platform validation should reject (surface a clear Invalid tool input) rather than forward a string to the bundle, and/or guide the model to emit a real array.
  • If schema mismatch: align the platform's validated schema with the bundle's.

Acceptance

  • A regression test in test/unit/coerce-input*.test.ts covering an Optional[list[dict]] arg supplied as a JSON string (both well-formed → coerced, and malformed → rejected by the platform with a clear error, never forwarded as a raw string to the bundle).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions