[BUG] VS Code: pasting a new image persists prior turn's image bytes in transcript

Resolved 💬 3 comments Opened Apr 26, 2026 by ojhurst Closed Apr 29, 2026
  • [x] I searched existing issues and didn't find a duplicate
  • [x] This is a single bug report (not multiple issues combined)
  • [x] I'm using the latest version of Claude Code

What's Wrong?

When a user pastes an image into the Claude Code VS Code chat input on a turn that follows another turn which also had an image, the new image renders correctly in the model's in-context view but the persisted transcript JSONL contains the previous turn's image bytes, byte-for-byte, instead of the newly pasted image.

Hooks and tools that read attachments from the transcript (UserPromptSubmit workflows, session forensics, content-addressed dedupe) therefore receive stale data on the second and any later image-bearing turns. The actually-pasted image is not stored anywhere on disk and cannot be recovered after the fact.

What Should Happen?

Each user turn that contains an image attachment should persist that turn's actual image bytes in the JSONL transcript. Two consecutive turns with two visibly different images should produce two distinct base64 payloads with different MD5 hashes.

Error Messages/Logs

No error is raised. The failure is silent — the JSONL accepts and stores the wrong bytes without any warning.

A UserPromptSubmit hook that decodes images from the transcript reports a hash collision and dedupes the second image as a duplicate of the first:

[<YYYY-MM-DD>T<HH:MM:SS>] transcript_grab: dedupe hit 941667**********************5847a6b

That is the symptom. The cause is upstream — the JSONL contains the wrong bytes.

Steps to Reproduce

  1. Open Claude Code in the VS Code extension. Start a fresh session.
  2. Paste image A (a clearly recognizable photo) into the chat input via Cmd+V and submit.
  3. Wait for the assistant turn to render fully.
  4. Paste image B (a clearly different photo) into the chat input and submit.
  5. Locate the session transcript JSONL at ~/.claude/projects/<project-slug>/<session-id>.jsonl.
  6. Find the two type: "user" lines whose message.content array contains an entry with type: "image".
  7. Decode each source.data from base64 to disk and open both files.

Expected: Two different images on disk, matching A and B.
Actual: Two copies of image A. Image B is nowhere in the JSONL.

Reproduction script (paste into a terminal after running through steps 1–4):

import json, base64, hashlib, sys
from pathlib import Path
tp = Path(sys.argv[1])  # session JSONL path
imgs = []
for line in tp.read_text().splitlines():
    try: d = json.loads(line)
    except: continue
    msg = d.get("message", {})
    if d.get("type") != "user" and msg.get("role") != "user":
        continue
    for c in msg.get("content", []):
        if isinstance(c, dict) and c.get("type") == "image":
            src = c.get("source", {})
            if src.get("type") == "base64":
                raw = base64.b64decode(src["data"])
                imgs.append((len(raw), hashlib.md5(raw).hexdigest()))
print(imgs)

If the bug is reproduced, the printed list shows two tuples with identical hash and length.

Claude Model

Opus 4.7 (1M context). Independent of model — the bug is in the chat persistence layer, not the model.

Is this a regression?

Don't know. The bug requires two image-paste turns in one session, which is uncommon, so it may have existed for many extension versions before being noticed.

Environment

- Claude Code Version: 2.1.118
- Platform: Anthropic API (Max subscription)
- Operating System: macOS 26.3.1
- VS Code: 1.117.0
- VS Code extension: anthropic.claude-code 2.1.120
- Terminal/Shell: VS Code integrated chat (not the CLI)

Additional Information

In the affected session, line 6 (turn 1) and line 44 (turn 2) of the JSONL both carry an image/png of ~290 KB with the same MD5 hash (941667**********************5847a6b, both turns). Decoding both base64 payloads to disk and opening them in Preview confirms they are pixel-identical, even though the user pasted two visibly different images.

Suspected cause: the VS Code chat-submit path appears to populate the new turn's content[].source.data field from a stale buffer — likely the last-attached image — instead of from the bytes of the just-pasted attachment. The model's in-context view of the new turn renders the correct image, suggesting the stale-buffer write happens at the persistence step, not at the model call.

View original on GitHub ↗

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