VS Code extension: clicking chat links to binary files (images) silently fails — unhandled showTextDocument rejection

Status Open
Reported on v2.1.123
Maintainer reply None cached
Activity 8 comments · opened Jul 25, 2026

Summary

In the VS Code extension's chat panel, clicking a markdown link to any binary file (PNG, JPG, PDF, …) silently does nothing. The extension resolves the path correctly, then tries to open it with vscode.window.showTextDocument(), which rejects binary files — and the rejection is unhandled, so the user gets no error, no feedback, nothing.

For image-heavy workflows (e.g. Claude generating/reviewing art assets and linking them for review) this makes every link the model emits dead, and the user has to copy paths by hand and navigate manually.

Environment

  • Claude Code VS Code extension: reproduced on every version I inspected from 2.1.123 (April) through 2.1.220 (current) — the code path is unchanged
  • IDE: Antigravity (VS Code fork), macOS 15 / Darwin 25.5.0 — but the bug is in the extension's own handler and should reproduce in stock VS Code (showTextDocument rejects binaries everywhere)

Repro

  1. Have Claude output a relative markdown link to any PNG in the workspace, e.g. [shot.png](assets/shot.png)
  2. Click it in the chat panel
  3. Nothing happens. The extension host log shows:
[error] CodeExpectedError: cannot open file:///Users/me/project/assets/shot.png.
Detail: File seems to be binary and cannot be opened as text

Links to text files (.md, .ts, …) work fine.

Root cause

In extension.js, the webview's open_file request is handled by openFile(filePath, location), which — after correct path resolution and a directory check that goes to revealInExplorer — unconditionally does:

ue.window.showTextDocument(n).then((i) => { /* selection/reveal logic */ })
  • showTextDocument rejects with CodeExpectedError for binary files
  • the promise has no rejection handler, so the failure is swallowed silently

No link-format workaround exists from the model side: file:// hrefs survive the markdown sanitizer but the webview host drops them, and custom-scheme deep links (vscode://file/... / fork equivalents) are stripped by the sanitizer's allowedSchemes list.

Suggested fix

Route non-text files through the generic opener, e.g.:

vscode.commands.executeCommand('vscode.open', uri)

either by sniffing known binary extensions before showTextDocument, or as a .catch() fallback when showTextDocument rejects (which also future-proofs any other non-text-openable resource). vscode.open shows images in the built-in image preview, which is presumably the expected UX.

At minimum, surfacing the rejection as an error notification would stop the failure from being silent.

Current workaround

Locally patching the installed extension.js to insert the extension-sniff + vscode.open fallback before the showTextDocument call fixes clicking for images — but the patch is wiped by every extension auto-update (which currently ship every 1–2 days), hence this report.

View original on GitHub ↗

3 Comments

Whillahub · 2 months ago

Independently confirmed, including the suggested fix.

  • Reproduced on extension v2.1.220 in both stock VS Code and Antigravity (macOS, darwin-arm64): clicking a chat link to a workspace-relative .png does nothing; .md links in the same folder open fine.
  • Applied the rejection-handler variant of the suggested fix to the installed extension.js — wrapping the openFile handler's showTextDocument(uri).then(...) with a rejection handler that falls back to vscode.commands.executeCommand("vscode.open", uri) — and image links immediately open in the built-in image preview in both editors. No side effects observed for text files, directories (revealInExplorer path untouched), or line-range selection links.

Can confirm the workaround pain too: the local patch is wiped by every extension auto-update, so we're re-applying it from a script. A .catch() fallback to vscode.open upstream would make this whole class of failure (any resource showTextDocument can't open) non-silent and correct.

wuesten-fuchs · 2 months ago

Confirming this on 2.1.219 (stock VS Code, macOS), plus one detail that may matter for triage: it is not limited to the markdown links Claude writes in prose. The webview routes several different affordances through the same open_file request, and therefore through the same openFile() handler:

  • the file name in a tool row header, e.g. the Read screenshot.png row (the Read tool renderer calls opener.open(file_path, …) from its header())
  • @-mention chips
  • attachment and IDE-selection chips above the transcript

All of them are equally dead for images, for the reason described above. That makes the blast radius wider than "links in an answer": having Claude read a screenshot and then clicking its name in the tool row is probably the most common way a user runs into this.

Worth noting because that tool-row variant was already filed separately as #37989 and closed as stale, and #76679 covers the same handler from yet another angle. Since all these call sites converge on that one function, a .catch() fallback to vscode.open in openFile() fixes every one of them at once.

tmone · 1 month ago

Still reproducible on the current extension (2.1.260-win32-x64) in stock VS Code 1.136.1 on Windows 10 — same code path.

Confirms @wuesten-fuchs' point: the Read <file>.png tool-row header link hits the same openFile handler, so screenshots Claude reads for verification cannot be opened from the transcript at all (text files and Edit rows open fine).

Local workaround I'm running (in openFile, right after the directory check):

if (/\.(png|jpe?g|gif|webp|bmp|ico|svg|pdf)$/i.test(resolvedPath)) {
  vscode.commands.executeCommand("vscode.open", uri);
  return;
}

vscode.open routes through the built-in image preview / custom editors, so it also fixes #81482 (PDF). A more general fix is to .catch() the showTextDocument rejection and fall back to vscode.open.

Showing cached comments. Read the full discussion on GitHub ↗