VS Code extension: file links in responses do not open when the path contains non-ASCII characters

Status Open
Maintainer reply None cached
Activity 1 comment · opened Aug 1, 2026

Summary

In the VS Code extension, clicking a file link in an assistant response does nothing when the path contains non-ASCII characters. ASCII-only paths open correctly.

My workspace uses Japanese filenames almost everywhere, so in practice no file link in a response has ever been clickable. There is no error and nothing in the output channel, so it just looks like the links are decorative.

Environment

  • Claude Code for VS Code: v2.1.220 (win32-x64)
  • VS Code: 1.131.0
  • OS: Windows 11

Repro

  1. Open a workspace containing a file with a non-ASCII name, e.g. docs/テスト.md, plus an ASCII one, e.g. docs/test.md.
  2. Get a response that links both, i.e. containing [docs/テスト.md](docs/テスト.md) and [docs/test.md](docs/test.md).
  3. Click the ASCII link → the file opens in the editor. ✅
  4. Click the non-ASCII link → nothing happens. ❌

Cause

The webview renders responses with react-markdown, whose mdast→hast step runs normalizeUri on link destinations, percent-encoding non-ASCII characters. So the rendered href becomes:

docs/%E3%83%86%E3%82%B9%E3%83%88.md

The click handler passes that href to fileOpener.open, and openFile in the extension host resolves it without decoding (extension.js, v2.1.220):

async openFile(e, t) {
  let r = On.isAbsolute(e) ? e : On.join(this.cwd, e);
  ...

It therefore stats <cwd>/docs/%E3%83%86%E3%82%B9%E3%83%88.md, which does not exist. The findFiles fallback further down is handed the same encoded string, so it produces no match either, and the click silently no-ops.

Suggested fix

Decode before resolving. To stay safe against filenames that legitimately contain %, only fall back to the decoded form when the literal path is missing and the decoded one exists:

let r = On.isAbsolute(e) ? e : On.join(this.cwd, e);
if (!Oi.existsSync(r)) {
  try {
    const d = decodeURIComponent(e);
    if (d !== e) {
      const r2 = On.isAbsolute(d) ? d : On.join(this.cwd, d);
      if (Oi.existsSync(r2)) { e = d; r = r2; }
    }
  } catch {}
}

I applied exactly this as a local patch and non-ASCII links now open correctly.

Note

Absolute paths are also rejected, though for a different reason: the href→path parser uses /^([^:#]+?)(?:[:#]L?(\d+)(?:-L?(\d+))?)?$/, and the [^:#] class means a Windows path such as C:\... never matches, so those clicks fall through to openExternal.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗