tools/claude-vscode-wrapper.c builds NODE_OPTIONS=--import file:///... by hand-escaping only spaces (:41-55). Any other character that is legal in a Windows path but meaningful in a URL survives unescaped, and node fails to load the preload — so cache-fix silently does nothing under VS Code on those hosts.
Found by @vsits-codex-review-agent while reviewing #294. Pre-existing on main; not introduced by that PR, which is why it is here and not there.
Measured
Node v24.11.1, through the production path (NODE_OPTIONS, not a direct --import flag), one directory per case:
path component wrapper's URL result
A#lice file:///…/A#lice/… ERR_MODULE_NOT_FOUND ← truncated at the fragment
B%ob file:///…/B%ob/… URI malformed (node:internal/url:1581)
C ecil file:///…/C%20ecil/… LOADED ← the one case handled
A%23lice (correctly encoded) LOADED
B%25ob (correctly encoded) LOADED
So # truncates the path at the fragment boundary and % throws outright, while both load fine once properly percent-encoded. The fix direction is not in doubt.
Read — reachability
The only variable in the constructed path is %APPDATA%. Microsoft's file-naming rules reserve < > : " / \ | ? *; # and % are not reserved, so both are legal in a profile directory name or in a redirected APPDATA (folder redirection to a share is routine in managed environments). A user whose profile or redirect target contains either character gets a wrapper that launches Claude Code with the preload silently absent.
Failure mode is the bad one: the wrapper still spawns node and CC starts normally, just with no cache-fix. There is no error the user would connect to this.
Suggested fix
Replace the hand-rolled space-escaping loop with a correct file-URL conversion — percent-encode everything outside the unreserved set rather than enumerating one character. Worth checking the same launch path for:
Notes
encoded_url sizing is fine and does not need changing — Codex measured it: worst case is a 259-char preload_url of all spaces expanding to 777 bytes, inside char[MAX_PATH * 3] (780) with the loop bound at -4.
Anyone picking this up will want a Windows host or a mingw cross-build; x86_64-w64-mingw32-gcc compiles the file clean at -Wall -Wextra.
Ref #294
— Proxy Builder
tools/claude-vscode-wrapper.cbuildsNODE_OPTIONS=--import file:///...by hand-escaping only spaces (:41-55). Any other character that is legal in a Windows path but meaningful in a URL survives unescaped, and node fails to load the preload — so cache-fix silently does nothing under VS Code on those hosts.Found by @vsits-codex-review-agent while reviewing #294. Pre-existing on
main; not introduced by that PR, which is why it is here and not there.Measured
Node v24.11.1, through the production path (
NODE_OPTIONS, not a direct--importflag), one directory per case:So
#truncates the path at the fragment boundary and%throws outright, while both load fine once properly percent-encoded. The fix direction is not in doubt.Read — reachability
The only variable in the constructed path is
%APPDATA%. Microsoft's file-naming rules reserve< > : " / \ | ? *;#and%are not reserved, so both are legal in a profile directory name or in a redirectedAPPDATA(folder redirection to a share is routine in managed environments). A user whose profile or redirect target contains either character gets a wrapper that launches Claude Code with the preload silently absent.Failure mode is the bad one: the wrapper still spawns
nodeand CC starts normally, just with no cache-fix. There is no error the user would connect to this.Suggested fix
Replace the hand-rolled space-escaping loop with a correct file-URL conversion — percent-encode everything outside the unreserved set rather than enumerating one character. Worth checking the same launch path for:
preload_urlischar[MAX_PATH]; anAPPDATAlong enough to exceed 260 chars gets cut andNODE_OPTIONSpoints somewhere that doesn't exist, again with no error. Unchanged by harden: use bounded strlcpy/snprintf in claude-vscode-wrapper.c... #294 (measured identical at lengths 1/200/250/255/258/259/260/300/1000)._spawnvp(_P_WAIT, "node", …)at:75resolvesnodethrough aPATHsearch rather than an absolute path. Pre-existing and not worsened by harden: use bounded strlcpy/snprintf in claude-vscode-wrapper.c... #294, but it is a trust boundary worth either tightening or documenting.Notes
encoded_urlsizing is fine and does not need changing — Codex measured it: worst case is a 259-charpreload_urlof all spaces expanding to 777 bytes, insidechar[MAX_PATH * 3](780) with the loop bound at-4.Anyone picking this up will want a Windows host or a mingw cross-build;
x86_64-w64-mingw32-gcccompiles the file clean at-Wall -Wextra.Ref #294
— Proxy Builder