VS Code extension: markdown links to files with non-ASCII names never open (percent-encoded href is never decoded)
Summary
In the VS Code extension's chat panel, clicking a markdown link that points to a file with a
non-ASCII filename does nothing. The link renders and is clickable, but no editor opens and no
error is shown.
This affects any repository whose filenames are not pure ASCII. In my case the entire docs/
tree uses Japanese filenames, so effectively none of Claude's file references are clickable.
Filing as a new issue per the closing message on #65548.
Relationship to existing issues
- #65548 — same defect, same root-cause analysis, reported 2026-06-05 against extension
2.1.161. Auto-closed as not planned on 2026-07-11 for inactivity, with an invitation to
open a new issue if still relevant. It is still relevant: unchanged in 2.1.233.
- #85641 (open) — links never open when the path contains a space. That is the same
missing-decode defect surfacing through %20 instead of %E3%83%90. The one-line fix
below resolves both classes, so the two issues should probably be tracked together.
Environment
| | |
| --- | --- |
| Extension | anthropic.claude-code 2.1.233 (darwin-arm64) |
| Claude Code CLI | 2.1.146 |
| VS Code | 1.133.0 |
| OS | macOS (Darwin 24.6.0), Apple Silicon |
Steps to reproduce
- Create a file with a non-ASCII name, e.g.
docs/バックログ.md - Have Claude reference it as a markdown link:
[バックログ.md](docs/バックログ.md) - Click the link in the chat panel
Expected: the file opens in an editor, as it does for an ASCII-named file.
Actual: nothing happens. No editor, no notification, no entry in the extension output channel.
Root cause
Traced through the shipped bundles in~/.vscode/extensions/anthropic.claude-code-2.1.233-darwin-arm64/.
1. webview/index.js — the markdown renderer percent-encodes the link destination.
Assistant messages are rendered with react-markdown. micromark's URL normalizer percent-encodes
every character with a code point >= 128, so the anchor's href becomes:
docs/50_Development/%E3%83%90%E3%83%83%E3%82%AF%E3%83%AD%E3%82%B0.md
2. The click handler passes that encoded string straight through as a filesystem path.
The anchor's onClick parses the href (the parser accepts it, because it still ends in .md)
and calls fileOpener.open(filePath) without decoding. decodeURI() does not appear anywhere
in the webview bundle, and none of the five decodeURIComponent call sites are on this path.
3. extension.js — openFile() does an existence check on the literal encoded string.
async openFile(e,t){
let r = mi.isAbsolute(e) ? e : mi.join(this.cwd, e);
...
let n = ke.Uri.file(r);
try{ if(Do.statSync(r).isDirectory()){ ... } }catch{}
ke.window.showTextDocument(n).then(...)
}
No file named %E3%83%90....md exists, so showTextDocument is called on a nonexistent URI and
the promise rejects unhandled — which is why the failure is completely silent.
Why the obvious workarounds don't help
- Pre-encoding the path in the markdown source — the normalizer preserves existing valid
%XX sequences, so the href is unchanged and the same failure occurs.
- Using an absolute path — same encoding, same failure.
- Using a
vscode://file/...URL — react-markdown's defaulturlTransformonly permits
http, https, irc, ircs, mailto, xmpp; vscode: is stripped to an empty href.
- Extension settings — the extension contributes 15 settings, none related to link resolution.
The only link form that still works is a link to a directory ([docs/foo/](docs/foo/)),
because directory names happen to be ASCII in my case and openFile() handles directories viarevealInExplorer.
Suggested fix
Decoding in openFile() covers every caller at once. Guarding it on "the decoded path actually
exists" keeps genuine % characters in filenames working:
async openFile(e,t){
try{
if(/%[0-9A-Fa-f]{2}/.test(e)){
let d = decodeURIComponent(e);
let r = mi.isAbsolute(d) ? d : mi.join(this.cwd, d);
if(Do.existsSync(r)) e = d;
}
}catch{}
// ...existing body unchanged
}
I applied exactly this to my local install and it resolves the issue. Verified against:
| Input | Result |
| --- | --- |
| docs/50_Development/%E3%83%90…%B0.md | opens バックログ.md |
| docs/50_Development/バックログ.md (unencoded) | opens, behaviour unchanged |
| /Users/…/260812_%E4%BD%9C…%81%8E.md (absolute) | opens 260812_作業引き継ぎ.md |
| 100%zz_broken.md (real file containing %) | left untouched, opens correctly |
%20 from #85641 takes the same path, so that case is covered by the same guard.
Decoding in the webview click handler instead would work equally well; openFile() just happens
to be the single choke point.
Separately, it would help if showTextDocument failures surfaced a notification or an
output-channel line. The silent failure is what made this hard to diagnose, and it is the reason
the same defect has now been reported three times (#65548, #85641, this one) with three different
descriptions of the symptom.