Tool-call XML emission silently drops parameters on bare tags / unescaped closing-tag substrings (diagnosis misrouted to receiving end)

Resolved 💬 3 comments Opened May 22, 2026 by hickeng Closed Jul 3, 2026

Summary

Tool-call XML emission silently drops parameters (or silently truncates calls) when the assistant's emitted XML contains specific malformed patterns. The parser proceeds with partially-parsed input; the receiving MCP tool then reports required field missing errors that point at the wrong layer. The model has no way to distinguish "I emitted wrong content" from "tool rejected valid content."

Empirical impact: a single session today (2026-05-22) hit this 4+ times. Vigilance fails reliably; this warrants Tier-0 (parser-level) handling.

Failure modes observed

  1. Bare <parameter> tags without antml: namespace prefix — silently stripped by parser rather than rejected loudly.
  1. Multi-line parameter values containing literal </invoke> or </parameter> substrings (e.g., heredoc-style content; embedded XML examples in documentation prose) — cause tag-balance violations that truncate the call mid-stream. The receiving tool sees partial input.
  1. Tag-imbalance at call boundary (extra closing tags, missing closers in nested values) — silently truncates parameters. Subsequent <parameter> blocks in the same invoke get parsed as text inside the truncated value rather than as separate parameters.

Reproduction pattern (sanitized)

When the model emits a multi-parameter tool call where one parameter value contains:

  • Inline XML examples in documentation prose
  • Heredoc-style content that includes </parameter> or </invoke> as text
  • Examples table demonstrating XML emission patterns

The parser appears to greedily consume subsequent sibling <parameter> blocks as text inside the offending value, then dispatches a partial call. The receiving MCP tool reports missing required fields. Diagnosis lands on the receiving end (Pydantic validator) when the root cause was at emission/parsing boundary.

Examples of leaked-text-in-output:

  • Body content ending with literal </body> (closing tag from authoring template)
  • Body content containing literal <parameter name="examples">[...]</parameter> as text
  • Body content containing literal </invoke> as suffix

Vigilance is insufficient because:

  • The behavior is silent (no error at emission)
  • The model can't see what was sent vs what was emitted
  • Per-context retraining produces compliance for ~1 turn then regression

Expected behavior

When the assistant emits malformed tool-call XML:

  1. Parser should REJECT loudly with a structured error that:
  • Identifies the specific malformation (bare tag / tag-imbalance / unescaped sibling-tag-in-value)
  • Returns location information (which parameter, which byte offset)
  • Surfaces the rejection to the model with diagnostic that distinguishes emission-error from tool-error
  1. Receiving tool should NOT see partial input — bad emission should fail at parse time, not at validation time.
  1. Optionally: attempt automated escape (encode literal </parameter> as &lt;/parameter&gt; inside values) and proceed with warning — surfaces fix-suggestion to the model.

Suggested fix direction (any of these would help)

  • Strict-mode parser that rejects bare <parameter> (without antml: prefix) loudly
  • Tag-balance validation before dispatching the tool call; reject on imbalance
  • Content-escape of literal closing-tag substrings inside parameter values (transparent to model, escapes during emission)
  • Diagnostic improvement: when receiving tool reports missing-field, parser should annotate response with "Note: tool-call XML had X malformation — emission did not match intent" if malformation was detected at parse time

Severity rationale

  • Silent failures are most expensive class of bug
  • Diagnosis misrouted — model debugs receiving end while the bug is upstream
  • Vigilance-resistant — has recurred 6+ times across 3 sessions in this project alone despite explicit attention
  • High blast radius — affects ALL tool calls with multi-parameter signatures + multi-line values (heredoc, document body content, code examples)

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗