[BUG] Image paste (Alt+V) always fails under WSL when login shell is fish — clipboard command run via $SHELL, which rejects "$(...)" in command position

Status Fixed / completed
Reported on v2.1.158
Maintainer reply None cached
Activity 3 comments · opened May 31, 2026 · closed May 31, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

On WSL2 with fish as the login shell, pasting a clipboard image (Alt+V) in the
Claude Code REPL always reports:

No image found in clipboard. Use ctrl+v to paste image

The clipboard genuinely contains an image. The failure is not in clipboard
access or WSL interop — it is that Claude executes its clipboard helper command through the user's login shell ($SHELL), and that command uses POSIX
command-substitution-in-command-position syntax that fish refuses to run.

The clipboard image detection/extraction command embedded in the bundle is:

"$(command -v powershell.exe 2>/dev/null || echo /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe)" \
    -NoProfile -NonInteractive -Sta \
    -Command 'Add-Type -AssemblyName System.Windows.Forms; if (-not [System.Windows.Forms.Clipboard]::ContainsImage()) { exit 1 }'

The leading "$(...)" is a command substitution in command position. POSIX shells accept it; fish does not, and aborts with exit code 127 before PowerShell is ever invoked. Claude interprets that failure as "no image in clipboard".

Notably, Claude's Bash tool already runs under bash (its shell snapshots are snapshot-bash-*.sh) — so only these auxiliary helper commands leak through to the raw login shell. Running the same helper under /bin/sh or bash works; only fish fails. The fix is to run these helper commands under /bin/sh (or bash) rather than $SHELL.

What Should Happen?

Pasting an image should detect and attach it regardless of the user's login
shell. Claude's internal/auxiliary shell commands should be executed with a
POSIX-compatible shell (e.g. /bin/sh), not the interactive login shell, since the commands are written in POSIX syntax that non-POSIX shells like fish cannot parse.

Error Messages/Logs

# Claude's UI toast:
No image found in clipboard. Use ctrl+v to paste image

# Reproducing the exact helper command Claude runs, under each shell:

$ fish -c '"$(command -v powershell.exe 2>/dev/null || echo /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe)" -NoProfile -NonInteractive -Sta -Command (...)'
fish: command substitutions not allowed in command position. Try var=(your-cmd) $var ...
# exit code: 127   <-- helper never runs; Claude concludes "no image"

$ /bin/sh -c '"$(command -v powershell.exe 2>/dev/null || echo /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe)" -NoProfile -NonInteractive -Sta -Command (...)'
# exit code: 0     <-- image detected correctly

# Proof the clipboard really holds an image (PowerShell via the same fallback path):
[System.Windows.Forms.Clipboard]::ContainsImage()  -> True
GetImage().Save(...)  -> valid PNG, 1019 x 322

# The login shell Claude inherits (from /proc/<claude-pid>/environ):
SHELL=/run/current-system/sw/bin/fish

Steps to Reproduce

  1. Use WSL2 with fish set as the login shell (echo $SHELL -> .../fish).
  2. Launch claude from that fish session.
  3. Take a screenshot on Windows (Win+Shift+S) so the Windows clipboard holds an image.
  4. Press Alt+V in the Claude Code prompt.

-> Toast: "No image found in clipboard. Use ctrl+v to paste image"

Confirm the cause:

  1. Quit, relaunch with a POSIX shell: env SHELL=/bin/bash claude
  2. Press Ctrl+V on the same screenshot -> image attaches correctly.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.158 (Claude Code)

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

WSL (Windows Subsystem for Linux)

Additional Information

Environment

  • OS: NixOS-WSL on WSL2, kernel 6.6.114.1-microsoft-standard-WSL2
  • Terminal: Windows Terminal hosting the WSL distro
  • Login shell: fish (SHELL=/run/current-system/sw/bin/fish)
  • WSL interop enabled; /mnt/c mounted; powershell.exe reachable via the

hardcoded fallback path. None of these are the problem.

View original on GitHub ↗

3 Comments

github-actions[bot] · 4 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/25779
  2. https://github.com/anthropics/claude-code/issues/62418

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

BastienClement · 4 months ago

This was actually Claude hallucinating.

Found it. The strace shows the real pipeline — and it's nothing I'd guessed. Claude tries clipboard methods in a || chain, Wayland/wl-paste before PowerShell:

Extraction (line 703):
xclip ... > screenshot.png 2>/dev/null \
  || wl-paste --type image/png > screenshot.png 2>/dev/null \
  || xclip ... image/bmp ... \
  || wl-paste --type image/bmp > screenshot.png 2>/dev/null \
  || "$(... powershell.exe ...)" ... GetImage ... base64 -d > screenshot.png

wl-paste is on PATH (your Nix wrapper adds wl-clipboard — claude-code.nix:211) and the strace confirms it runs (lines 708, 713). The || chain stops at the first command that exits 0. My strong hypothesis: on WSLg, wl-paste --type image/png exits 0 but writes an empty/garbage file, so the chain short-circuits and the working PowerShell fallback is never reached — leaving an invalid screenshot.png → "No image found."

That explains every failed fix: shell and PATH were irrelevant because PowerShell is never invoked. Let me verify with exit codes from the trace, the leftover file, and live wl-paste behavior.

  Read 1 file, ran 1 shell command

This is the real root cause, and it's traced end-to-end from the strace — not a guess. My first two diagnoses were both wrong; here's what actually happens.

Claude's clipboard extraction is a || fallback chain that stops at the first command exiting 0, and it tries Wayland before PowerShell:

xclip image/png       → 127 (not installed; truncates file empty)
wl-paste image/png    → exit 1  (WSLg Wayland has no image/png)
xclip image/bmp       → 127 (not installed; truncates file empty)
wl-paste image/bmp    → exit 0  ← STOPS HERE, writes a BMP
powershell GetImage→PNG  ← never reached

Proof from your machine:
- strace exit codes: pid 4099588 (wl-paste image/png) exited 1, pid 4099590 (wl-paste image/bmp) exited 0 → short-circuit.
- The leftover file /tmp/claude-1000/claude_cli_latest_screenshot.png is actually a PC bitmap, Windows 3.x format, 220×156×24 — a BMP, not a PNG.
- Live wl-paste -l on your WSLg session offers only text/html and image/bmp (no image/png).

So Claude ends up with a BMP (in a .png-named file) that it can't decode → "No image found." The working PowerShell PNG path is never reached because wl-paste --type image/bmp "succeeds" first. This is the mechanism behind the closed issue #50552.

Why all three earlier attempts failed to matter: the strace shows these commands run via /bin/sh (not $SHELL) — so fish/SHELL was never involved — and PowerShell is never invoked, so PATH to it was irrelevant. The decisive factor is that wl-paste is on Claude's PATH at all — added by your own wrapper (claude-code.nix:211, for background-agent wl-copy).
github-actions[bot] · 2 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.