Model glitches mid-\uXXXX escape in tool-call arguments (emits \ + raw CJK char), making the whole input unparsable (InputValidationError)

Status Open
Reported on v2.1.215
Maintainer reply None cached
Activity 1 comment · opened Jul 20, 2026

Summary

When the model spells non-ASCII tool-call arguments as ASCII \uXXXX escape sequences, it can glitch mid-escape — emitting \ followed by the raw character instead of the four hex digits (e.g. \로 instead of ). That single invalid escape makes the entire tool input unparsable, and the CLI rejects the call with:

InputValidationError: <tool> was called with input that could not be parsed as JSON.
Common causes: unescaped backslashes ..., unescaped control characters, or truncated output.

This is a distinct, precisely-diagnosable failure mode — not generic "malformed JSON on long inputs". We recovered the exact malformed bytes and pinpointed the defect.

Evidence: the exact malformed bytes

Our harness spawns the CLI programmatically (Agent SDK, stream-json). A tool call with long Korean arguments failed:

InputValidationError: mcp__verbs__invoke was called with input that could not be parsed as JSON.
You sent (first 200 of 2614 bytes): {"uri": ..., "message": "thesingularity-reader 후속 수정 ...

The CLI preserves the full raw unparsed input in the project transcript (~/.claude/projects/<project>/<session>.jsonl, in the assistant message's tool_use block as input.__unparsedToolInput.raw, capped at 2048 chars). Running the recovered raw input through a strict JSON parser pinpoints the defect at char 1712:

Invalid \escape: line 1 column 1713 (char 1712)
...패널은 자동으\로 닫힌다...
                ^^

The model chose to spell the entire Korean payload as ASCII \uXXXX escapes (360 of them in this call) and, mid-word, emitted \ followed by the raw character 로 (U+B85C) instead of the digits ub85c. It was spelling 으로 (으로) and glitched halfway through the second escape. \로 is an invalid JSON escape, so the entire ~2.6 KB input fails to parse.

Everything else about the input is well-formed. Escape-spelled CJK arguments normally parse fine — we have many successful calls with the same spelling in the same sessions. The failure is not the escape spelling per se, nor input length, nor tool-call framing: it is a sampling glitch that mixes the escape spelling with the raw-character spelling inside one escape sequence.

The escape spelling is chosen randomly, and is strictly worse

The model is fully capable of emitting native Korean as raw UTF-8 — most successful calls in the same sessions do exactly that — but it non-deterministically opts into the fully-escaped \uXXXX spelling for some calls. That choice degrades both cost and reliability:

  • Token inflation: each CJK character becomes a 6-char ASCII sequence spelled across multiple tokens, so an escaped payload is several times longer than the equivalent raw UTF-8.
  • Accuracy/reliability: each escape is a multi-token spelling of a single character, so a ~2.6 KB fully-escaped payload is hundreds of independent opportunities to glitch mid-escape; raw UTF-8 spelling has none.

This mechanically explains the anecdotal correlation between long CJK/unicode-heavy arguments and InputValidationError (as reported in #69522): length doesn't cause the failure, but the escape spelling scales the number of chances for it.

Frequency

Sweeping all our local transcripts for __unparsedToolInput: 13 unparsable tool inputs total, of which 1 is this mixed-escape glitch — the rest are ordinary malformations (literal control characters in strings, trailing commas, extra data). So the mode is real but rarer than generic JSON glitches.

Recovery behavior

The model retried the same logical call ~12 s later, this time spelling the arguments as raw UTF-8, and it succeeded. This matches the "shorter/cleaner retry succeeds" observation in #69522.

Possible mitigations

  • Since the raw input is already preserved (__unparsedToolInput.raw), the CLI could attempt a targeted repair for this specific class: a \ followed by a raw non-ASCII, non-escape character inside a JSON string is unambiguously recoverable (drop the stray \ or re-encode the character).
  • Alternatively/additionally, discouraging the fully-escaped \uXXXX spelling for long non-ASCII payloads (constrained sampling or prompting) removes the opportunity for the glitch entirely.

Repro / diagnosis tip

Grep ~/.claude/projects/**/*.jsonl for __unparsedToolInput and run the raw field through any strict JSON parser — the parse error position shows exactly what the model emitted.

Environment

  • Claude Code CLI: 2.1.215, spawned via @anthropic-ai/claude-agent-sdk 0.3.201 (stream-json)
  • Model: claude-opus-4-8
  • OS: macOS (Darwin 25.5.0)
  • Language: Korean arguments (CJK), MCP tool call (mcp__verbs__invoke)

Related

Originally posted as a comment on #69522 (https://github.com/anthropics/claude-code/issues/69522#issuecomment-5019286004). Split into its own issue because that report frames the problem as an open question mixing an observed failure with an unreproduced start-token-framing hypothesis, whereas this is a confirmed, evidence-backed diagnosis of one specific serialization defect.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗