fix(kitty): translate paths for a Windows-side terminal under WSL - #377
Open
szsolt wants to merge 1 commit into
Open
fix(kitty): translate paths for a Windows-side terminal under WSL#377szsolt wants to merge 1 commit into
szsolt wants to merge 1 commit into
Conversation
szsolt
force-pushed
the
wsl-transmit-file-path
branch
from
July 31, 2026 05:37
b03d937 to
21f5ee8
Compare
When Neovim runs inside WSL but the terminal emulator is a Windows program -- in practice WezTerm, the one Windows terminal implementing this protocol -- transmit_medium=file hands it a path the two processes don't agree on: Neovim sends /tmp/foo.png and the terminal resolves it against the Windows filesystem, where it doesn't exist. The transmit silently does nothing, and the placement that follows draws whatever still occupies that image id -- so the symptom is usually a stale or unrelated image, not a blank. Verified with a bare protocol probe: t=f with /tmp/x.png renders nothing, while the same command with \\wsl.localhost\<distro>\tmp\x.png renders correctly, as does t=d with inlined bytes. So the path is the variable, not the payload. Translate via wslpath -w, cached per path since the same file is retransmitted across renders. Gate on the terminal actually being Windows-side rather than on WSL alone: a Linux-native terminal under WSLg shares our filesystem and cannot open a UNC path, so translating there would break a working setup. Linux-side terminals set their own env vars in our process (WEZTERM_PANE, KITTY_WINDOW_ID, ...) and a Windows-side one cannot, so their absence is the signal. Verified both ways: WezTerm on Windows gets the UNC path, kitty under WSLg keeps /tmp/... and both render. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
szsolt
force-pushed
the
wsl-transmit-file-path
branch
from
July 31, 2026 05:44
21f5ee8 to
239f62d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When Neovim runs inside WSL but the terminal emulator is a Windows program β in practice
WezTerm, the one Windows terminal with a working kitty-graphics implementation (Windows
Terminal has not implemented it; kitty
has no Windows build) β images either render as the wrong image or don't appear at all.
The kitty backend transmits with
transmit_medium = file(t=f), which hands the terminala path and lets it open the file itself. That assumes both processes see the same
filesystem. Here they don't: Neovim sends
/tmp/foo.png, and the Windows-side terminalresolves it against the Windows filesystem, where it doesn't exist.
The transmit therefore does nothing. Because the failure is silent, the placement that
follows draws whatever still occupies that image id β which is why the symptom is usually a
stale or unrelated image rather than an obvious blank.
Not affected: a Linux-native terminal under WSLg, where both sides share the filesystem and
t=falready works β see the gating note below, since that case must be excluded ratherthan merely left alone. Also unaffected is
t=d, which reads the file on the Neovim side.Evidence
Bare protocol probe outside Neovim, same image, same terminal (WezTerm on Windows,
Ubuntu-24.04 under WSL2), varying only how the data is referenced:
t=fwith/tmp/probe.pngt=fwith\\wsl.localhost\Ubuntu-24.04\tmp\probe.pngt=dwith inlined bytesSo the path is the variable, not the payload or the placement.
Change
Translate the path with
wslpath -wimmediately before it's handed to the terminal, gatedon the terminal actually being Windows-side:
Gating on WSL alone would be wrong, not just imprecise: a Linux process cannot open a
\\wsl.localhost\...path, so a Linux-native terminal under WSLg would go from working tobroken. Verified locally β
cat "$(wslpath -w /tmp/f)"fails, and the path has to beconverted back with
wslpath -uto be readable.The discriminator is that a terminal running Linux-side sets its own variables in our
environment, while a Windows-side one has no way to. On this machine
TERM_PROGRAM=WezTermis set but
WEZTERM_PANEis empty, which is exactly the Windows-side signature; aLinux-native WezTerm always sets
WEZTERM_PANE.Results are cached per path, since
wslpathis a subprocess and the same file isretransmitted across renders.
No effect outside that combination, or if
wslpathisn't available β all return the pathunchanged.
Reproducing
Worth knowing before testing:
init.luaskipswrite_graphicsentirely on a transmit-cachehit, so an image already resident in the terminal keeps rendering correctly even with this
fix disabled. Use a fresh terminal window, or delete stored images (
a=d,d=A) first,otherwise the bug looks absent.
Notes
The translation sits in
write_graphics, which is the single point where a path reachesthe terminal (
init.lua:63is the only call passing one; the other calls send control-onlypayloads). Remote images are covered without extra code, as they're downloaded to
vim.fn.tempname()first.One limitation worth naming: the env-var check infers where the terminal runs rather than
observing it, since the process on the other end of the pty isn't introspectable from here.
It's correct for the terminals listed, but a Linux-side terminal that sets no identifying
variable would be misclassified as Windows-side and get a UNC path it can't open. Falling
back to
t=don a failed transmit would be the robust fix, but the protocol gives nosuccess signal to react to without a round-trip. Happy to add other terminals' variables if
you know of ones worth including.