InputValidationError: long unicode-escaped tool arguments fail JSON parse (AskUserQuestion) — retry with shorter input succeeds

Status Open
Reported on v2.1.181
Maintainer reply None cached
Activity 10 comments · opened Jun 19, 2026
Note: This report separates what was directly observed in one session from a broader hypothesis based on recurring user experience. They may or may not be the same underlying defect.

---

Part 1 — Directly observed (reproduced this session)

AskUserQuestion was called with a long Korean question whose arguments serialized into many \uXXXX unicode escapes. The call failed with:

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

A shorter, single retry of the same logical call succeeded. Same intent, shorter/cleaner serialization -> passed.

What is certain from this: the model sometimes emits malformed JSON in the tool-call arguments for long / heavily unicode-escaped inputs, causing a parse/validation failure. This part is reproduced and logged.

What is NOT certain: whether the failure is in argument serialization specifically, vs. the tool-call start token / framing. I cannot distinguish these from the surface error alone.

Part 2 — Hypothesis (recurring across sessions, NOT reproduced here)

Across multiple prior sessions (anecdotal, user-reported), a related-looking failure has been observed where the tool-call start token is emitted as plain text (fragments resembling ordinary words), breaking parsing — and where the error appears to repeat across consecutive turns as if the model imitates its own previous malformed output.

This pattern was NOT reproduced in the current session. It is included only as context in case it correlates with Part 1. Treat it as a hypothesis, not evidence.

Suspected amplifying conditions (correlational, not proven)

  • Multiple tool calls in a single message (parallel)
  • Long arguments, especially long non-ASCII / unicode-escaped text
  • High effort level (xhigh) producing longer outputs
  • Large persistent context (large CLAUDE.md, many MCP tools loaded)

Workarounds that empirically reduce frequency

  • One tool call per message (serial)
  • After a failure, retry once with a shorter/cleaner call (worked in Part 1)
  • Lower effort level (xhigh -> high)
  • Reduce tool surface area (remove unused MCP connectors)

Environment

  • Claude Code: 2.1.181 (WinGet install)
  • Model: Opus 4.8 (claude-opus-4-8)
  • OS: Windows 11 (10.0.26200)
  • Shell: Git Bash (POSIX) + PowerShell 5.1

Ask

Could maintainers confirm whether long unicode-escaped tool arguments are a known cause of InputValidationError JSON parse failures, and whether that is related to the broader start-token framing issue described in Part 2? Happy to capture a fuller transcript next time Part 2 reproduces.

View original on GitHub ↗

10 Comments

github-actions[bot] · 2 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/64506
  2. https://github.com/anthropics/claude-code/issues/64060
  3. https://github.com/anthropics/claude-code/issues/61670

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

solar-web-design · 2 months ago

Additional evidence (second session, stronger pattern)

Reviewed another session's full transcript (a session heavy on Outlook MCP calls — reading ~13 emails + attachments). Out of 28 total tool failures, the dominant failure cluster (13 of them) was malformed MCP tool arguments, which is the same class as Part 1 above but with a much clearer signature.

Strong signals — model emitted malformed arguments

These are not "wrong target" errors; the arguments themselves were structurally broken:

| Count | Signature | Why it points to arg serialization |
|---|---|---|
| 5x | get_email400 Bad Request (very long message-id in URL) | Long ID arguments getting mangled in the request |
| 2x | Missing required argument: attachment_id (get_attachment) | Required arg dropped from the tool call |
| 1x | Missing required argument: account_id (get_email) | Required arg dropped from the tool call |
| 1x | Invalid account_id format (get_attachment) | Arg value malformed |
| 2x | Invalid non-printable ASCII character in URL, '\n' at position 209 (get_email) | A literal newline was injected into an argument value — the clearest evidence |

The '\n' at position 209 case is the smoking gun: the model inserted a stray newline into a long argument value, breaking the call. This is the same underlying behavior as the original AskUserQuestion JSON parse failure (long argument → malformed serialization), just surfacing through a different tool and a different downstream validator.

Weaker / possibly-unrelated

  • 1x get_attachment404 Not Found — could be a genuinely missing attachment rather than a malformed arg; included only for completeness, not as evidence.

Updated read on this issue

  • Confirmed (now across 2 sessions, ~13 cases): long / complex tool arguments (long IDs, many tokens, non-ASCII) intermittently get dropped or corrupted (incl. injected \n) during tool-call argument serialization.
  • Still unconfirmed (Part 2): the "start-token emitted as plain text" + self-imitation cascade — not reproduced in either reviewed session.

Suggested repro angle for maintainers

Stress get_email / get_attachment (or any MCP tool taking long ID-like args) with long argument values and observe whether stray \n or dropped required args appear in the outgoing call. The non-printable ASCII '\n' in URL validation error is a reliable detector.

solar-web-design · 2 months ago

Correction + strong new evidence: the "self-imitation cascade" (Part 2) IS reproduced

In my earlier comment I said the Part 2 hypothesis (start-token / self-imitation cascade) was "not reproduced." That was wrong — I had only counted errors individually and missed the temporal cascade structure. Re-reading the transcript, there is a clear, logged cascade.

The cascade (single task: download one XLSX attachment, ~5 consecutive failures)

get_attachment → Missing required argument
get_email      → Missing required argument        (same failure again)
get_email      → Invalid non-printable ASCII '\n' at position 209
get_email      → '\n' at position 209             (right after saying it would send it on one line)
get_attachment → Missing required argument         (again)

Smoking-gun behavioral evidence — the model's own words mid-cascade

  • After the first \n failure, the assistant stated: "a newline got mixed into the ID; I'll send it on a single line"and then emitted \n again on the very next call.
  • Then: "\n keeps getting inserted at position 209. The email_id is 200+ chars, so position 209 is inside the ID. This looks like the tool framework inserting line breaks into long argument values."

So the model recognized the defect, announced a fix, and immediately repeated the same malformed output. That is the self-imitation cascade — now an observed fact, not a hypothesis.

Root cause (refined)

When multiple long base64 ID arguments (200+ chars) are sent in one tool call, the argument serialization intermittently:

  • injects a \n into the middle of a value (→ non-printable ASCII '\n' in URL), or
  • drops the last argument (→ Missing required argument).

This happens before the MCP server receives anything — the arguments are already corrupted at the model/harness serialization layer. The MCP server then correctly rejects the malformed input; it is not the cause.

Trigger conditions (now better characterized)

  • Multiple long arguments in a single call (the "last argument dropped" pattern appears specifically here)
  • Argument values >200 chars (the \n lands around position 209, inside the long ID)
  • The cascade persists across consecutive turns even after the model explicitly tries to correct it

Why this matters for triage

The self-correction-then-repeat behavior suggests the corruption is not something the model can avoid by "trying harder" — it recurs deterministically under the trigger, which points to the serialization/encoding layer rather than model reasoning.

solar-web-design · 1 month ago

Part 2 reproduced at scale — stray token flood on SHORT-argument, non-MCP tools (self-imitation cascade)

The Part 2 hypothesis ("start-token emitted as plain text + self-imitation across turns") — previously marked not reproduced — has now reproduced, at large scale, in a different environment. Crucially, this time with short-argument built-in tools (Read/Bash/Edit/Playwright), no MCP and no long base64 IDs involved, which answers the open question of whether the defect is confined to long arguments: it is not.

Measured from the session transcript (JSONL, full scan)

| Metric | Value |
|---|---|
| Session length | 1,693 events / 342 assistant text blocks |
| Stray token (court) total occurrences | 1,113 |
| Messages affected | 28 |
| Worst single message | 387 repetitions (4,162 chars) |

Behavior observed

  • At the point where a tool call should be emitted, the plain-text token court leaks instead of (or interleaved with) the tool-call framing. The tool call is delayed or replaced by runs of the stray token.
  • The model repeatedly announced it would self-correct ("this is a glitch, ignore it", "calling the tool now") and then immediately re-emitted the same stray token — the same self-imitation cascade documented earlier (previous evidence: 5 consecutive failures; this time: hundreds of repetitions across 28 messages).
  • User interrupts did not clear it; the loop re-ignited on the next response. Switching the model (/model) stopped it instantly, consistent with the corrupted pattern living in the accumulated self-context.

Context before onset (possible triggers)

  • Very long session (multiple auto-compactions), repeated large outputs (a ~3MB HTML artifact regenerated several times).
  • The loop began immediately after ~10 consecutive failed retries of a screenshot tool (element capture kept missing its target) — i.e. repeated tool failure appears to have been the ignition, matching the earlier "do not imitate the previous broken output" failure mode.

Updated read

  • Part 1 (long-arg serialization corruption: injected \n, dropped required args) and Part 2 (start-token leakage + self-imitation) now both have direct transcript evidence.
  • The leaked token differs per incident (count/call-like fragments before, court now) — so it is not one magic word; the tool-call framing itself intermittently decodes as plain text, and the model then locks onto its own corrupted output.
  • Practical mitigation that worked: model switch / fresh session. In-session self-correction does not work once the corrupted pattern accumulates in context.

Environment: Claude Code on Windows 11 (10.0.26200), model Opus 4.8 (claude-opus-4-8), long-running session with high context pressure. Happy to share the sanitized transcript excerpts if useful.

solar-web-design · 1 month ago

Follow-up: exact substitution point captured (transcript forensics)

Located the first leak in the transcript. The failure mechanism is now precisely characterized:

재생성하고, 이번엔 fullPage 스크린샷으로 Rmax 부분만 정확히 잘라 확인합니다.

court
<invoke name="Bash">
<parameter name="command">cd "e:/Coding/sungsin" ...
  • The plain-text token court was emitted exactly where the tool-call opening frame should be, and the entire call body (<invoke>/<parameter> XML) then streamed as inert plain text inside the text block — the harness never saw a tool call at all.
  • So this is a distinct failure mode from the long-argument corruption reported earlier (\n injected at position 209, dropped args — where the call was established): here a single token substitution at the framing boundary inertizes the whole call.
  • Zero in-message recovery: across all 34 assistant messages containing the stray token, 0/34 also contained a real tool_use block. Once the substitution lands, the model only re-emits text-mode pseudo-calls or bare stray tokens within that message.
  • Ignition context correction: the 10 events immediately preceding the first leak were all successful Edit/Read cycles (not failures). The broader window had ~10 failed screenshot retries, but the direct precondition looks like high-frequency tool-call cycling in a very long, multiply-compacted session rather than an error immediately prior.
  • Weak signal, hypothesis only: leaked words across incidents (count/call anecdotally, court confirmed) are all short c- English tokens — possibly tokenizer near-neighbors of the framing sequence. Not verifiable from outside; flagging in case it helps navigate logit/tokenizer data internally.

What would confirm root cause (only visible internally): token-level logs/logits at the framing decision point in long-context sessions — distinguishing (a) sampling near-miss on the special token, (b) harness decode-path issue, (c) compaction artifact. From decoded transcripts these are indistinguishable.

---

Addendum — layer discrimination (evidence was already at hand; correcting my "indistinguishable" claim above):

| Evidence | If harness/API decode bug (b) | Observed |
|---|---|---|
| /model switch (harness & decode path unchanged) | new model should break identically | instant, complete cure; all calls clean after |
| Onset pattern | deterministic (same input → same break) | hundreds of clean calls, then stochastic onset; successful calls interleaved even during the flood |
| Leaked artifact | fragments of the framing string expected | an intact ordinary vocabulary token (court) |
| Amplification | a parser doesn't imitate context | self-imitation cascade (model-native behavior) |

→ Strongly favors (a) model-side sampling near-miss on the tool-call framing token, with the harness faithfully rendering the broken emission as text. Suggests triage should start at model/sampling under long-context pressure rather than CLI parse code. Not 100% conclusive without raw token logs, but the Bayesian lean is heavy.

solar-web-design · 1 month ago

Two actionable notes: a model-free mitigation, and a newer-model robustness observation

1. Harness-level mitigation (no model change required)

The failure signature is cheap to detect at the harness: an assistant text block containing tool-call XML (<invoke name=...> / parameter markup) that never materialized as a tool_use block. That is always a defect — it has no legitimate occurrence.

A harness that detects this could break the cascade at ignition (n=1) instead of letting it amplify (we measured 0/34 in-message recovery once the pattern establishes):

  • drop the inert pseudo-call text from the context window (don't let the model see its own corrupted emission), and/or
  • inject a short corrective system note and force a fresh emission attempt.

Since amplification is driven by in-context self-imitation, removing the corrupted output from what the model sees next is the lever. Server-side constrained decoding at tool-call positions would prevent it outright, but the harness-side detector looks like the cheap, shippable version.

2. Newer model appears robust — including against an already-contaminated context

The strongest datapoint from our incident: when the user switched models mid-session (/model), the successor model inherited the same context containing 1,113 stray-token occurrences across 28 messages — and never imitated it once. All subsequent tool calls were clean, immediately.

Same harness, same contaminated context, different weights → clean behavior. This (a) further confirms the defect is model-side emission, and (b) suggests the failure mode may already be effectively addressed in newer-generation models — i.e., resolution may arrive via model succession even if older weights are never patched. If that matches internal expectations, the mitigation in (1) mainly matters for sessions pinned to older models.

solar-web-design · 1 month ago

Correction & full-session timeline: definitive tally, geometric escalation curve, trigger refinement

My earlier figures (1,113 total / worst 387) were a mid-flood snapshot — measured while the cascade was still growing. Definitive numbers from the complete transcript:

| Metric | Final value |
|---|---|
| Stray-token total (affected model, entire session) | 4,759 |
| Worst single message | 2,928 (next: 710, 387) |
| After model switch | 15 occurrences — **all verified as mentions (quoted inside bug-analysis prose/snippets); zero bare-line or call-boundary emissions** |

Timeline (the part that changes the trigger hypothesis)

Day 1  09:01–11:57  1,060 events, 2.5h continuous heavy tool cycling → ZERO anomalies
       ~4 days dormant~
Day 5  ~09:27       session resumed
       09:45        first substitution — only ~18 min after resume
       09:45–09:50  sporadic ×1–3 (model kept working, announcing self-corrections)
       09:50–09:57  geometric escalation: 8→17→21→25→103→125→178→387
       09:59        peak ×2,928 in one message
       10:04        /model switch → emissions stop instantly

Refined trigger read: it is not continuous-work duration. A 2.5-hour heavy-tooling stretch on the same session was perfectly clean; the failure ignited 18 minutes after resuming a 4-day-dormant, heavily-accumulated/compacted context, then grew geometrically (×1 → ×2,928 within 14 minutes). This points at context saturation/compaction state at resume as the precondition — possibly worth reproducing internally by resuming long-dormant, near-limit sessions rather than by running long fresh ones.

The successor-model observation from my previous comment also survives stricter scrutiny: post-switch occurrences of the stray token were individually classified — every one is a quotation inside forensic discussion, none at emission positions.

theand · 1 month ago

Corroborating from a different environment — same failure, much higher frequency.

Environment (differs from OP → looks cross-platform, not Windows-specific):

  • Claude Code 2.1.211
  • Model: Opus 4.8 (claude-opus-4-8[1m])
  • OS: macOS (Darwin 25.5.0)
  • Effort xhigh; large global CLAUDE.md; many MCP servers loaded

Frequency: hitting this dozens of times a day, not occasional. Consistent trigger: AskUserQuestion whose Korean question/option text serializes into long \uXXXX-escaped JSON.

Verbatim error:

InputValidationError: AskUserQuestion was called with input that could not be parsed as JSON.
You sent (first 200 of 2245 bytes): {"questions": [{"question": "이 수정(로컬 커밋 완료)을 ...
Common causes: unescaped backslashes in file paths (use / or \\), unescaped control characters, or truncated output.

The payload was 2245 bytes for a one-line Korean question — each Hangul char becomes a 6-char \uXXXX escape, so short prompts balloon into escape-dense JSON where one malformed escape / stray backslash / control char fails the whole parse. A single retry with the same intent parses and succeeds, matching Part 1.

Amplifiers I can confirm on my side: xhigh effort, large persistent context (big global CLAUDE.md + many MCP tools), and option/description text containing (...) or file paths with \. Trimming the payload and avoiding backslash paths noticeably lowers the rate.

Adding this mainly as evidence it's not Windows-specific and is frequent for non-ASCII (Korean) locales, which points at argument serialization (Part 1) rather than the Part 2 start-token hypothesis.

sorryhyun · 1 month ago

We hit the same failure and can answer the Part 1 uncertainty (serialization vs. framing): it is the argument serialization — a single token-level glitch inside a \uXXXX escape sequence.

Definitive evidence: the exact malformed bytes

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

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). Recovering it and running it through a 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 으로 as 으로 and glitched halfway through the second escape. \로 is an invalid JSON escape, so the entire ~2.6 KB input is unparsable.

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

Corroborating details

  • Same recovery pattern as reported here: the model retried the same logical call ~12 s later, this time spelling the arguments as raw UTF-8, and it succeeded.
  • Sweeping all our transcripts for __unparsedToolInput: 13 unparsable tool inputs total, of which only this one is the mixed-escape glitch (the rest are ordinary malformations — literal control characters in strings, trailing commas, extra data). So this failure mode is real but rarer than generic JSON glitches.
  • The correlation with long non-ASCII arguments makes sense mechanically: each escape sequence is a multi-token spelling of a single character, so a 2.6 KB fully-escaped payload is hundreds of opportunities to glitch mid-escape, whereas raw UTF-8 spelling has none.

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)

Repro tip for anyone else hitting this: grep your ~/.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.

ethanPiggyHuang · 1 month ago

Corroborating evidence: same failure family reproduces in Traditional Chinese (zh-TW), not Korean-specific

Reviewed my own transcript history (~/.claude/projects/**/*.jsonl) across the last 2 weeks (2026-07-15 to 2026-07-28) and found 15 occurrences of InputValidationError: JSON parse failed, all in AskUserQuestion or nearby tools, spanning CLI versions 2.1.206 through 2.1.218, on macOS (darwin-arm64). All content is Traditional Chinese. This confirms the bug is not Korean-specific — it reproduces with a different CJK language, across many consecutive CLI releases (not one bad build).

Impact observed: zero. Every occurrence self-recovered — the CLI/agent retried automatically and moved on. No session got stuck. Sharing purely as corroborating forensic data for prioritization, since Part 1 and Part 2 of the original report were each based on a single session.

Part 1 confirmed — mid-\uXXXX escape glitch (matches #79339 exactly)

One example, run through a strict JSON parser to pinpoint the exact byte:

JSONDecodeError: Invalid \uXXXX escape: line 1 column 569 (char 568)
context: '...加上去的,但驗證用\u UI(Task N)...'

Same mechanism as #79339: the model is mid-way through spelling a Chinese word as \uXXXX, then instead of completing the 4 hex digits it drops straight into literal ASCII text (\u immediately followed by a space + the literal letters UI, not the intended hex digits) — one incomplete escape breaks the entire payload.

Tool affected: AskUserQuestion. 7 of my 15 occurrences match this exact sub-pattern, byte sizes ranging 347–2354 bytes — i.e. not exclusively a "very long input" thing, a 347-byte call failed too.

Part 2 confirmed — tool-framing token leaks into JSON value (matches the "self-imitation cascade" comments below)

JSONDecodeError: Expecting value: line 2 column 1 (char 15)
context: '{"questions": \n<parameter name="question">[...internal content redacted...]'

Right where a JSON array value is expected after "questions":, the model instead emits a literal newline followed by <parameter name="question"> — tool-invocation framing syntax leaking into the argument position, same family as the <invoke name="Bash"> leak documented further down this thread. 2 of my 15 occurrences match this sub-pattern.

---

Happy to share more raw __unparsedToolInput.raw transcript excerpts (with project-internal content redacted) if it helps narrow down the exact trigger.