[BUG] Claude code 2.1.83 and above broken on WSL1

Open 💬 24 comments Opened Mar 25, 2026 by mbasilepa

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?

When starting claude code via 'claude' in a Windows WSL 1 environment, version 2.1.83 fails to start with a '-bash: /home/user/.local/bin/claude: cannot execute binary file: Exec format error'. version 2.1.81 works on WSL 1. This is not using the npm install, but the curl install (curl -fsSL https://claude.ai/install.sh | bash)

What Should Happen?

claude code (cli) should start without an error

Error Messages/Logs

-bash: /home/user/.local/bin/claude: cannot execute binary file: Exec format error

Steps to Reproduce

1) create a wsl 1 environment:
(WSL version: 2.6.3.0
Kernel version: 6.6.87.2-1
WSLg version: 1.0.71
MSRDC version: 1.2.6353
Direct3D version: 1.611.1-81528511
DXCore version: 10.0.26100.1-240331-1435.ge-release
Windows version: 10.0.19045.6466
Ubuntu (Default)

2) install claude:
(curl -fsSL https://claude.ai/install.sh | bash)

3) start claude
(claude)

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.81

Claude Code Version

2.1.83

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

WSL (Windows Subsystem for Linux)

Additional Information

_No response_

View original on GitHub ↗

24 Comments

github-actions[bot] · 3 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/38644

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

mbasilepa · 3 months ago

This is probably the same root issue as #38644. This is failing on an 'auto update' silently until claude is started again. Running 2.1.81, silent update, exit 2.1.81. Restart claude (now 2.1.83) and failure.

folofjc · 3 months ago

I can confirm this. I had to roll back to 2.1.81. Claude even told me to come here and file a bug report 😆

"You're back up and running! The rollback to 2.1.81 worked.
To recap: version 2.1.83 introduced a regression that breaks WSL 1, while 2.1.81 works fine. And consider filing a bug report at https://github.com/anthropics/claude-code/issues with the details — kernel version 4.4.0-19041-Microsoft (WSL 1), Ubuntu 24.04, and that 2.1.83 fails with Exec format error while 2.1.81 works fine."

Igor-FP · 3 months ago

Root cause analysis: comparing ELF program headers between 2.1.81 (works) and 2.1.84 (broken):

2.1.81 segment 08: GNU_STACK (standard, zero file size)
2.1.84 segment 08: LOAD with .bun section (~129 MB, alignment 0x1000)

The new .bun LOAD segment was introduced in the Bun runtime update between 2.1.81 and 2.1.83. WSL 1's ELF loader cannot handle this segment layout. This is the same class of bug as Ubuntu LP#1966849 (gzip broken on WSL 1 due to non-standard LOAD alignment).

Full readelf -l diff available in #39385.

AFN7 · 3 months ago

WSL1 Workaround: Force Node.js Fallback

After digging into the minified extension.js (VS Code extension), I found that the extension already has a Node.js fallback path built in. The function getClaudeBinary() works like this:

  1. Look for the native binary at resources/native-binary/claude
  2. If not found → fall back to resources/claude-code/cli.js running via Node.js

The problem is that the binary exists on disk (so step 1 succeeds), but WSL1 can't execute it. The extension never reaches the fallback.

The Fix

Force the extension to use the Node.js fallback by:

  1. Renaming the broken native binary (so the extension can't find it)
  2. Creating the fallback directory structure with cli.js from the npm package

Script

#!/bin/bash
# WSL1 Claude Code VS Code Extension Fix
# Fixes "Exec format error" caused by Bun ELF binary incompatibility
# Run after each extension update

set -euo pipefail

# Detect WSL1
if [[ ! -f /proc/version ]] || ! grep -qi "microsoft" /proc/version || grep -qi "microsoft-standard" /proc/version; then
    echo "Not WSL1 - not needed."; exit 0
fi

EXTENSIONS_DIR="$HOME/.vscode-server/extensions"
NPM_DIR="$HOME/.npm-global/lib/node_modules/@anthropic-ai/claude-code"

# Find latest extension
EXT_DIR=$(ls -1d "$EXTENSIONS_DIR"/anthropic.claude-code-*-linux-x64 2>/dev/null | sort -V | tail -1)
[[ -z "$EXT_DIR" ]] && { echo "No extension found."; exit 1; }

VERSION=$(basename "$EXT_DIR" | sed 's/anthropic.claude-code-\(.*\)-linux-x64/\1/')
NATIVE_BIN="$EXT_DIR/resources/native-binary/claude"
FALLBACK_DIR="$EXT_DIR/resources/claude-code"

echo "Patching Claude Code v${VERSION} for WSL1..."

# Step 1: Install/update npm package to match extension version
if [[ ! -f "$NPM_DIR/cli.js" ]]; then
    npm install -g "@anthropic-ai/claude-code@${VERSION}" || npm install -g "@anthropic-ai/claude-code"
else
    NPM_VER=$(node -e "console.log(require('$NPM_DIR/package.json').version)" 2>/dev/null)
    if [[ "$NPM_VER" != "$VERSION" ]]; then
        npm install -g "@anthropic-ai/claude-code@${VERSION}" 2>/dev/null || true
    fi
fi

# Step 2: Disable native binary
if [[ -f "$NATIVE_BIN" ]]; then
    mv "$NATIVE_BIN" "${NATIVE_BIN}.bak"
    echo "Native binary disabled."
fi

# Step 3: Create Node.js fallback structure
mkdir -p "$FALLBACK_DIR"
ln -sf "$NPM_DIR/cli.js" "$FALLBACK_DIR/cli.js"
ln -sfn "$NPM_DIR/node_modules" "$FALLBACK_DIR/node_modules"
[[ -d "$NPM_DIR/vendor" ]] && ln -sfn "$NPM_DIR/vendor" "$FALLBACK_DIR/vendor"

# Verify
if node "$FALLBACK_DIR/cli.js" --version >/dev/null 2>&1; then
    echo "Done! $(node "$FALLBACK_DIR/cli.js" --version)"
    echo "Reload VS Code window to apply (Ctrl+Shift+P > Reload Window)"
else
    echo "ERROR: Fallback verification failed."
    exit 1
fi

Auto-patch on updates

Add this to your ~/.bashrc to automatically detect and patch new extension versions on every terminal open:

# WSL1 Claude Code auto-patch
_wsl1_cc_check() {
    [[ -f /proc/version ]] || return
    grep -qi "microsoft" /proc/version || return
    grep -qi "microsoft-standard" /proc/version && return
    for d in "$HOME/.vscode-server/extensions"/anthropic.claude-code-*-linux-x64; do
        [[ -d "$d" ]] || continue
        [[ -f "$d/resources/native-binary/claude" ]] || continue
        "$d/resources/native-binary/claude" --version >/dev/null 2>&1 && continue
        [[ -L "$d/resources/claude-code/cli.js" ]] && continue
        echo "[WSL1] New Claude Code extension detected - run: bash ~/wsl1-claude-fix.sh"
    done
}
_wsl1_cc_check; unset -f _wsl1_cc_check

Why this works

The extension's getClaudeBinary() method checks for resources/native-binary/claude first. If absent, it looks for resources/claude-code/cli.js and runs it via Node.js (process.execPath). This was the execution path in v2.1.81 and earlier. By removing the native binary and providing cli.js, we restore the old behavior.

Suggestion for Anthropic

A proper fix would be a ~5 line change in getClaudeBinary(): after finding the native binary path, try executing it with --version. If it fails (exit code !== 0), fall back to the Node.js path. This would make the extension self-healing on WSL1 and any other platform where the Bun binary can't run.

Tested on WSL1 (kernel 4.4.0, Ubuntu) with extension v2.1.85 — both the VS Code sidebar and editor panels work correctly via the Node.js fallback.

folofjc · 3 months ago

I updated to 2.1.88 on a different machine through npm. I don't get the error, so it might have fixed it even though the release notes do not mention this bug.

mbasilepa · 3 months ago

This is still broken via 'curl -fsSL https://claude.ai/install.sh | bash'
Setting up Claude Code...
bash: line 151: /home/userdir/.claude/downloads/claude-2.1.87-linux-x64: cannot execute binary file: Exec format error

Although I do note this is version 2.1.87 and not 2.1.88 being pulled

folofjc · 3 months ago
Although I do note this is version 2.1.87 and not 2.1.88 being pulled

I noticed that they pulled 2.1.88 as well. It was up for a few hours when it updated for me.

folofjc · 3 months ago
Although I do note this is version 2.1.87 and not 2.1.88 being pulled

Whoops. Looks like this is why they pulled it: CLI

mbasilepa · 3 months ago

2.1.90 installed via curl still broken

curl -fsSL https://claude.ai/install.sh | bash
Setting up Claude Code...
bash: line 151: /home/userdir/.claude/downloads/claude-2.1.90-linux-x64: cannot execute binary file: Exec format error

AdrianEddy · 3 months ago

I have the same issue
here's a workaround:

cd $HOME/.vscode-server/extensions/anthropic.claude-code-2.1.92-linux-x64/resources/native-binary
mv claude claude.bin
cat > claude <<'EOF'
#!/bin/sh
exec /lib64/ld-linux-x86-64.so.2 "$(dirname "$0")/claude.bin" "$@"
EOF
chmod +x claude
Niehztog · 3 months ago

for my setup the following workaround works fine.

Add this to your ~/.bashrc:

claude() {
    /lib64/ld-linux-x86-64.so.2 "$(readlink -f /home/user/.local/bin/claude)" "$@"
}

(replace the term user with your appropiate username.
This way auto updates replacing the claude symlink will continue to work.

sakilo1990 · 2 months ago

This started happening to me after the self-update went to 2.1.113 but fine after rolling back to 2.1.112

luckin403 · 2 months ago

Has this issue not been resolved yet?
I am struggling because I am forced to use WSL1 due to my specific development environment requirements.
According to the Claude Code reference, WSL1 is still listed as a supported environment.

yanoryuichi · 2 months ago

I have the same issue.

sakilo1990 · 2 months ago
for my setup the following workaround works fine. Add this to your ~/.bashrc: claude() { /lib64/ld-linux-x86-64.so.2 "$(readlink -f /home/user/.local/bin/claude)" "$@" } (replace the term user with your appropiate username. This way auto updates replacing the claude symlink will continue to work.

Worked for be as well but because I use fnm I had to modify it to the following:

claude() {
  /lib64/ld-linux-x86-64.so.2 "$(readlink -f "$(whence -p claude)")" "$@"
}
markusjevringflowers · 2 months ago

Even if that alias fixes existing installations, it's not possible to actually install the application from scratch with this issue.

she-jab · 2 months ago
Even if that alias fixes existing installations, it's not possible to actually install the application from scratch with this issue.

It is possible, you just run the installer until it crashes, and then you can run the extracted package the same way and it will work. It's annoying, but it's doable since it's a 1 time thing.
Something like /lib64/ld-linux-x86-64.so.2 "$HOME/.claude/downloads/claude-2.x.xxx-linux-x64" install

markusjevringflowers · 2 months ago
> Even if that alias fixes existing installations, it's not possible to actually install the application from scratch with this issue. It is possible, you just run the installer until it crashes, and then you can run the extracted package the same way and it will work. It's annoying, but it's doable since it's a 1 time thing. Something like /lib64/ld-linux-x86-64.so.2 "$HOME/.claude/downloads/claude-2.x.xxx-linux-x64" install

I got around it by reading install.sh and downloading the right file manually. I just mentioned it to say that the proposed work-around only works for situations when it's already installed.

Walkman100 · 2 months ago

This is still an issue, at least with the apt package:

~$ apt list --installed claude-code
claude-code/unknown,now 2.1.128-1 amd64 [installed]
~$ claude --version
-bash: /usr/bin/claude: cannot execute binary file: Exec format error
~$ /lib64/ld-linux-x86-64.so.2 /usr/bin/claude --version
2.1.128 (Claude Code)
sashton · 1 month ago

Using ld-linux as a workaround breaks grep/find/rg — here's a fix

The ld-linux launch workaround gets Claude running, but causes a secondary bug: all grep, find, and rg tool calls fail with:

error while loading shared libraries: -G: cannot open shared object file: No such file or directory

Root cause: Claude Code determines CLAUDE_CODE_EXECPATH from process.execPath, which reads /proc/self/exe. When launched as ld-linux-x86-64.so.2 ~/.local/bin/claude, the process's /proc/self/exe resolves to the linker, not the claude binary. So CLAUDE_CODE_EXECPATH gets set to /lib64/ld-linux-x86-64.so.2.

The injected grep shim then does:

ARGV0=ugrep "$CLAUDE_CODE_EXECPATH" -G ... "$@"

Which becomes ARGV0=ugrep /lib64/ld-linux-x86-64.so.2 -G ... — the linker receives -G as a library name to load, and fails.

Fix: Two small compiled pieces:

  1. ~/.local/bin/claude-dispatch — a C program that checks argv[0] and dispatches correctly: if called as ugrep/bfs/rg, it chains to ld-linux --argv0 <tool> ~/.local/bin/claude <args>. Otherwise it does a normal launch.
  1. ~/.local/lib/claude-preload.so — an LD_PRELOAD library that intercepts readlink("/proc/self/exe") and returns the dispatch binary path instead of the linker path. This makes CLAUDE_CODE_EXECPATH point to the dispatcher.

The preload and dispatch are two separate pieces because each solves a different part of the problem: the preload is the only thing that can intercept /proc/self/exe, and the compiled dispatch binary is the only thing that correctly receives argv[0] from the shim (a shell script would lose it due to shebang rewriting).

Setup:

# Compile the dispatch binary
gcc -o ~/.local/bin/claude-dispatch claude-dispatch.c

# Compile the preload library
mkdir -p ~/.local/lib
gcc -shared -fPIC -o ~/.local/lib/claude-preload.so claude-preload.c -ldl

# Add to ~/.zshrc or ~/.bashrc
claude() {
  LD_PRELOAD="$HOME/.local/lib/claude-preload.so" \
    /lib64/ld-linux-x86-64.so.2 "$HOME/.local/bin/claude" "$@"
}

Replace YOUR_USER with your username in both C files.

<details>
<summary>claude-dispatch.c</summary>

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <stdlib.h>

#define CLAUDE_BIN "/home/YOUR_USER/.local/bin/claude"
#define LD_LINUX   "/lib64/ld-linux-x86-64.so.2"

int main(int argc, char *argv[], char *envp[]) {
    char name_buf[256];
    strncpy(name_buf, argv[0], 255);
    name_buf[255] = '\0';
    char *name = basename(name_buf);

    if (strcmp(name, "ugrep") == 0 || strcmp(name, "rg") == 0 || strcmp(name, "bfs") == 0) {
        char **new_argv = malloc((argc + 4) * sizeof(char *));
        new_argv[0] = LD_LINUX;
        new_argv[1] = "--argv0";
        new_argv[2] = name;
        new_argv[3] = CLAUDE_BIN;
        for (int i = 1; i < argc; i++) new_argv[3 + i] = argv[i];
        new_argv[3 + argc] = NULL;
        execve(LD_LINUX, new_argv, envp);
    } else {
        char **new_argv = malloc((argc + 2) * sizeof(char *));
        new_argv[0] = LD_LINUX;
        new_argv[1] = CLAUDE_BIN;
        for (int i = 1; i < argc; i++) new_argv[1 + i] = argv[i];
        new_argv[1 + argc] = NULL;
        execve(LD_LINUX, new_argv, envp);
    }

    perror("execve failed");
    return 1;
}

</details>

<details>
<summary>claude-preload.c</summary>

#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#define DISPATCH_PATH "/home/YOUR_USER/.local/bin/claude-dispatch"

typedef ssize_t (*readlink_fn)(const char *, char *, size_t);
typedef ssize_t (*readlinkat_fn)(int, const char *, char *, size_t);

ssize_t readlink(const char *path, char *buf, size_t bufsiz) {
    if (strcmp(path, "/proc/self/exe") == 0) {
        size_t len = strlen(DISPATCH_PATH);
        if (len > bufsiz) len = bufsiz;
        __builtin_memcpy(buf, DISPATCH_PATH, len);
        return (ssize_t)len;
    }
    static readlink_fn orig;
    if (!orig) orig = (readlink_fn)dlsym(RTLD_NEXT, "readlink");
    return orig(path, buf, bufsiz);
}

ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
    if (strcmp(path, "/proc/self/exe") == 0) {
        size_t len = strlen(DISPATCH_PATH);
        if (len > bufsiz) len = bufsiz;
        __builtin_memcpy(buf, DISPATCH_PATH, len);
        return (ssize_t)len;
    }
    static readlinkat_fn orig;
    if (!orig) orig = (readlinkat_fn)dlsym(RTLD_NEXT, "readlinkat");
    return orig(dirfd, path, buf, bufsiz);
}

</details>

geoff-maddock · 1 month ago
This started happening to me after the self-update went to 2.1.113 but fine after rolling back to 2.1.112

Does 2.1.112 work for you on WSL1?

geoff-maddock · 1 month ago

I have been running on 2.1.112 on WSL1 for the past few weeks. Anyone been able to use a newer version?

geoff-maddock · 27 days ago

Not here to spam, but sharing my experience trying to run the latest version:

gmaddock@EXP21623:/c/Users/geoff.maddock/code/hopper$ npm install -g @anthropic-ai/claude-code@2.1.181

added 1 package, removed 2 packages, and changed 1 package in 3s
gmaddock@EXP21623:/c/Users/geoff.maddock/code/hopper$ claude
bash: /home/gmaddock/.nvm/versions/node/v22.22.0/bin/claude: cannot execute binary file: Exec format error
gmaddock@EXP21623:/c/Users/geoff.maddock/code/hopper$ npm install -g @anthropic-ai/claude-code@2.1.112

added 2 packages, removed 1 package, and changed 1 package in 2s

2 packages are looking for funding
  run `npm fund` for details
gmaddock@EXP21623:/c/Users/geoff.maddock/code/hopper$ claude
 ▐▛███▜▌   Claude Code v2.1.112
▝▜█████▛▘  Opus 4.7 (1M context) with xhigh effort · Claude Max