MCP channel notification ingestion truncates mid-surrogate, persisting a lone surrogate into the transcript (400 no low surrogate in string)

Resolved 💬 2 comments Opened Jun 18, 2026 by mydandyandy Closed Jun 22, 2026

Summary

When a long notification arrives over an MCP channel (--channels), Claude Code truncates the ingested message to ~1313 characters while building the transcript records. If that truncation boundary falls between the two halves of a UTF-16 surrogate pair (e.g. an emoji like 🔑 = U+D83D U+DD11), the low surrogate is dropped and an orphaned high surrogate is persisted into the session .jsonl. Every subsequent request to the Anthropic API then re-serializes that lone \ud83d, and the API's strict JSON parser rejects it:

400 The request body is not valid JSON: no low surrogate in string: line 1 column NNNNNN

The session is bricked until the transcript is manually repaired — the bad byte poisons every retry.

Impact

Permanent session death from a single inbound channel message. Not recoverable by retry; requires hand-editing the .jsonl to strip the lone surrogate.

Root cause (observed)

  • The corrupted strings appeared twice in the transcript: a queue-operation .content record and the user .message.content, both exactly 1313 chars, both with a lone high surrogate U+D83D at index 1301, with text continuing after it.
  • The originating message was ~600 chars longer than the stored 1313 — i.e. it was truncated at ingest, and the cut landed mid-surrogate.
  • The truncation is in Claude Code's MCP-channel-notification → transcript ingestion, not in the channel sender or the MCP plugin (verified: the plugin transmits the full payload; only a separate 200-char preview field is sliced elsewhere).

Suggested fixes

  1. Make the ingest truncation surrogate-aware — never cut between a high/low surrogate; drop a trailing lone surrogate after truncating.
  2. Sanitize lone surrogates before persisting any content to the transcript (belt-and-suspenders).

Environment

Claude Code CLI on Windows 11, MCP channels plugin.

---

Related: #36503 (also in the --channels / MCP-ingest area, though a distinct symptom).

View original on GitHub ↗

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