[BUG] Attaching/pasting an image fails with 400 ... str is not valid UTF-8: surrogates not allowed

Status Closed — duplicate
Maintainer reply None cached
Activity 8 comments · opened Jun 20, 2026 · closed Aug 19, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Whenever I attach or paste an image into the Claude Code chat, the request fails with:
API Error: 400 The request body is not valid JSON: str is not valid UTF-8: surrogates not allowed: line 1 column 1 (char 0)
This happens with any image, including fresh screenshots saved to the Desktop — so it is not a corrupted file. The error is reproducible across multiple different projects and has occurred consistently over several days. It looks like the client-side image-attachment encoding path is producing invalid UTF-8 (lone/unpaired surrogates) in the JSON request body.

Claude Code running inside the Claude Desktop app for Mac
App version: 1.14271.0
macOS (Apple Silicon)

What Should Happen?

The image is encoded correctly (base64 image block) and the request succeeds.

Error Messages/Logs

API Error: 400 The request body is not valid JSON: str is not valid UTF-8: surrogates not allowed: line 1 column 1 (char 0)

Steps to Reproduce

Open Claude Code inside the Mac desktop app.
Take a screenshot and either paste it (Cmd+V) or attach the saved PNG file.
Send the message.
The request fails with the 400 ... surrogates not allowed error.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

1.14271.0 (c8f4d8) 2026-06-18T05:47:57.000Z

Platform

Other

Operating System

macOS

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

7 Comments

github-actions[bot] · 2 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/68737
  2. https://github.com/anthropics/claude-code/issues/63885

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

houleixx · 2 months ago
For this kind of "request body is not valid JSON" error, the key debugging artifact is the exact request body Claude Code sends when the image is attached.

ccglass can capture Claude Code traffic locally and show the actual request body, response/error body, latency, and per-turn context. That may help confirm whether the invalid surrogate is introduced before the API call, during image serialization, or in a surrounding message block.

Project: https://github.com/jianshuo/ccglass
jeanmatheussouto · 2 months ago

Reproducible on Linux/X11 too — so this is not macOS/desktop-specific; it points to the client-side image-attachment encoding path in core.

Environment

  • Claude Code 2.1.197
  • Node v24.6.0
  • Ubuntu 22.04 (kernel 6.8.0), session type X11 (DISPLAY=:1)

Evidence isolating it to the client (not the source image or the clipboard layer)

The clipboard held a perfectly valid PNG, confirmed with xclip before pasting:

$ xclip -selection clipboard -t TARGETS -o
image/png
TARGETS
TIMESTAMP

$ xclip -selection clipboard -t image/png -o > clip.png
$ file -b clip.png
PNG image data, 3127 x 702, 8-bit/color RGBA, non-interlaced   # 102202 bytes
$ xxd -l 8 clip.png
00000000: 8950 4e47 0d0a 1a0a   .PNG....

So the image bytes and the X11 clipboard are fine. The lone/unpaired surrogates are introduced during Claude Code's image-attachment serialization, before the request leaves the client — matching the line 1 column 1 (char 0) position.

Workaround that avoids the bug: referencing the file via @/path/to/image.png (or dragging the file in) instead of pasting with the clipboard. That code path does not hit the error.

Emryn-Ben · 1 month ago

Also hitting this on macOS: 26.5.2.
Claude Code: 2.1.199.
Happens on every clipboard paste — error fires but image arrives on retry.

Workaround that works for me: reading the file directly by path instead of pasting from clipboard. Screenshots save to ~/Pictures, then reference via file path in chat. Avoids the serialization bug entirely.
(Work around which takes longer)

Broke 2026-07-02, was working on the 1st of July and prior.

goodwinb99 · 1 month ago

Confirming on macOS, VSCode extension (native/embedded, not a shelled-out terminal claude), v2.1.201 — same 400 ... surrogates not allowed: line 1 column 1 (char 0). In my case it fired when a pasted screenshot was submitted as part of a queued message (image pasted while Claude was still mid-task), after which every subsequent turn returned the same 400.

Two datapoints that may help localize it:

  • The corruption is in-memory during request assembly, not persisted. I byte-scanned the session .jsonl transcript and it's clean UTF-8 with no lone surrogates anywhere — the attached image's base64 is pure ASCII. So the bad surrogate is introduced when the request body is built/encoded, not stored on disk.
  • Simply restarting VSCode and reopening the Claude tab fully recovered the session, with the screenshot intact — no transcript edit, no re-attach, nothing. Because it's the embedded extension there's no --resume step; the tab just picked up where it left off and worked. So this behaves like a transient encode-time bug rather than corrupted/persisted session state, which distinguishes it from the reports where the session is permanently bricked.
goodwinb99 · 1 month ago

Follow-up: I've now hit this with no image attached, mid-stream, on the same setup — so the image path may not be the whole story. I'm behind Netskope Secure Web Gateway (TLS inspection), and on the failing turns there's a long pause followed by the whole response arriving in a single burst rather than streaming incrementally — i.e. the gateway is buffering the full SSE response body. With the gateway off / on a non-Netskope VM, streaming works and the error disappears.

Working theory: the gateway buffers and re-chunks the text/event-stream and splits a multi-byte UTF-8 sequence at a chunk boundary → lone surrogate → replayed on the next request → 400 surrogates not allowed. Full write-up with a differential test and a suggested client-side fix (decode UTF-8 across chunk boundaries / sanitize lone surrogates) in #64777 — quite possibly the same root cause as the image-attach path here.

allenfortes · 1 month ago

Confirming this on Windows 11, VS Code extension, CLI + extension both on v2.1.205 (latest as of 2026-07-09).

Reproduction:

  1. Paste a screenshot (via built-in Windows Snipping Tool) into the Claude Code chat along with a short prompt. Works fine.
  2. Paste a second screenshot into the same conversation. This request fails with:

API Error: 400 The request body is not valid JSON: str is not valid UTF-8: surrogates not allowed: line 1 column 1 (char 0)

  1. From that point on, every subsequent request in the same session fails with the same error — even plain text messages with no image attached. The corrupted turn appears to stay in conversation history/context and gets re-sent (and re-rejected) on every following request. Starting a new session is the only way to recover.

Ruled out while investigating:

  • Not a remote/VDI/RDP session — local machine.
  • Same screenshot tool (Windows Snipping Tool) used on a second laptop that does not reproduce the issue.
  • No clipboard manager installed.
  • Not a DPI/display-scaling issue — tested at 100% scaling on the affected laptop, still reproduces.
  • Reproduced on both v2.1.204 and v2.1.205.

This looks distinct from the emoji/truncation-boundary surrogate fixes shipped in v2.1.172/v2.1.187 — no emoji or special characters involved, just repeated image pastes.

Likely cause found: the affected laptop has Netskope's endpoint agent installed and running (EPDLP endpoint-DLP module + STAgent SSL-interception/traffic-steering module). Netskope's DLP inspects clipboard content, and its SSL-interception agent decrypts/inspects/re-encrypts HTTPS traffic (including this app's API calls) for content scanning before forwarding it. That would explain the 400 coming from the API itself rather than a client-side crash — the request body is likely being corrupted in-transit during Netskope's inspection/re-encoding of the payload, which is more likely to surface on larger binary payloads like base64-encoded images than plain text. My other, unaffected laptop does not have this agent installed. If others hitting this are on a corporate network with a TLS-inspecting/DLP proxy (Netskope, Zscaler, etc.), that's worth checking — the fix on my end will likely be an SSL-inspection/DLP bypass exception for Anthropic's API domains rather than anything in Claude Code itself.

(Secondary, likely unrelated: the affected laptop has no admin rights so runs Claude Code's "native" self-contained binary rather than a global npm install — mentioning in case it's relevant, though the Netskope finding above seems the stronger lead.)

Also reproduces in the plain claude CLI, not just the VS Code extension — same error, same "every subsequent request fails" behavior. Since the CLI and the VS Code extension use different mechanisms for attaching/pasting images, this rules out anything specific to VS Code's own clipboard-paste handling and points at something shared by both entry points: either the core CLI binary's request/encoding logic, or the network path (which for me means Netskope, intercepting every claude.exe flow to api.anthropic.com regardless of which entry point launched it).

Showing cached comments. Read the full discussion on GitHub ↗