Malformed tool-call generation: stray `court` token + non-namespaced <invoke> rejected by parser
Summary
Claude Code (model claude-opus-4-8) intermittently emits a malformed tool call: a stray literal token court followed by a non-namespaced <invoke> / <parameter> block instead of the correct namespaced control-token form. The harness rejects it with "Your tool call was malformed and could not be parsed. Please retry." The call never executes; the turn is wasted. Once it occurs, it recurs in a self-copy loop for the rest of the session.
Severity / impact
Medium. No data corruption or side effects (the call does not execute), but each occurrence wastes a full turn, and the self-reinforcing loop can stall multi-step work until the session is reset.
Environment
- Product: Claude Code (CLI)
- Model:
claude-opus-4-8 - OS: macOS (Darwin 25.5.0)
- Heavy custom harness: many PostToolUse/PreToolUse hooks, rules files, skills.
Exact malformed output (verbatim from transcript)
court
<invoke name="Edit">
<parameter name="replace_all">false</parameter>
<parameter name="file_path">/Users/.../some_file.md</parameter>
<parameter name="old_string">...</parameter>
<parameter name="new_string">...</parameter>
</invoke>
Two defects vs. a valid call:
- Stray token
courton its own line where the function-call start should be. - Missing namespace —
<invoke>/<parameter>instead of the namespaced control-token form.
Expected vs. actual
- Expected: a valid namespaced tool-call control-token sequence that the harness parses and executes.
- Actual: plain-text, non-namespaced
<invoke>block prefixed withcourt→ parser rejects.
Where observed
- Onset at ~72% through a single long (~3269-record) session; none earlier in the same session.
- 27 malformed generations in that session; tools affected:
Edit,Write,Agent(failure is independent of which tool was intended). - No deterministic user-side repro; it is intermittent.
Frequency (measured across full local transcript corpus)
- 1 of 1106 sessions across all local projects exhibits the signature.
- Within that session: 27 malformed generations; the stray token was always
court. - The recurrence is consistent with the model copying its own malformed call from context.
Investigation — ruled out (so this is not a local-config artifact)
- No copy-source in config: no bare/non-namespaced
<invoke>example exists in any rules file, skill, or hook. courtnot seeded anywhere: the literal token appears in no config file.- No context compaction occurred in the affected session.
- Not hook-triggered: the first malformed call followed a clean
Readtool result; no PostToolUse hook output preceded it. - Model: ran on
claude-opus-4-8(not a forced-smaller-model path).
Hypothesized mechanism (reporter's assessment — needs confirmation)
The start-of-tool-call special token appears to have been decoded as an ordinary vocabulary token (surfacing as the word court) instead of as the control token, after which the model continued in the plain-text form of a tool call (non-namespaced <invoke>) rather than the control-token form. The exact token-id → court mapping cannot be confirmed from the transcript alone.
Suggested checks / mitigations
- Whether a specific token id adjacent to / part of the tool-call start sentinel decodes to
courtunder certain sampling/context conditions. - Whether long-context pressure or repetitive identical tool calls raise the probability of this mis-decode.
- A harness-side mitigation: detect a non-namespaced
<invoke>/court-prefixed block in model output and auto-recover (re-prompt or repair) instead of only returning a parse error that the model then copies.
Honest open gap
I recall seeing this in fresh/short sessions too, but no fresh-session instance exists in the local transcript corpus — only the one long session above. The fresh-session variant could not be characterized from available data.