Claude Code exit codes

What each Bash tool exit code means in Claude Code — including 126, which doesn't mean what it usually does, and the Windows bug behind most exit code 1s.

Last reviewed · verified against the pingdotgg/t3code repo

When Claude Code's Bash tool fails, it reports the shell's exit code. Most of those codes carry their standard Unix meaning — but one of them doesn't, and that one causes the most confusion.

Quick reference#

CodeStandard meaningIn Claude Code
1Generic failureOften a Windows shell-resolution bug, not your command — below
2Misuse of a shell builtinSame; usually a real syntax error
126Command found but not executable⚠️ Also a deliberate security blockbelow
127Command not foundSame; a PATH problem — below
137128 + 9 → killed by SIGKILLUsually out-of-memory, or an interrupt — below
143128 + 15 → terminated by SIGTERMA graceful stop: timeout or cancellation
130128 + 2 → SIGINTCtrl-C

Anything above 128 is 128 + signal number. That arithmetic is the whole trick: 137 - 128 = 9 (SIGKILL), 143 - 128 = 15 (SIGTERM). If you see an unfamiliar code above 128, subtract 128 and look up the signal.

Exit code 126: the one that differs#

Everywhere else in Unix, 126 means the file exists but isn't executable. In Claude Code it is also used for a deliberate security refusal:

text
Error: Exit code 126 [BLOCKED — DO NOT RETRY]
Command uses eval or $()/backticks at command position, which is blocked
regardless of allowlist. This is a permanent security restriction, not a
transient error.

Read the message rather than the number. "Permanent security restriction, not a transient error" means retrying will never work — and neither will adding the command to an allowlist, because the block sits above allowlist evaluation.

The trigger is a command substitution in command position — the shell deciding what to run based on the output of another command:

bash
eval "$SOMETHING"          # blocked
$(which python) script.py  # blocked — substitution decides the binary
`which node` app.js        # blocked — same thing, backticks

Rewrite so the binary is literal:

bash
python script.py           # fine
node app.js                # fine
bash
# need the resolved path? resolve it in a separate step, then use it
which python

If the real problem is a genuinely non-executable file, that's the ordinary 126 and the fix is ordinary too:

bash
chmod +x ./script.sh

Exit code 1: usually not your command#

Exit code 1 is generic, so it's the most-reported and the least self-explanatory. The distinguishing symptom that matters: exit code 1 with no output at all, on commands that work fine when you run them yourself.

That combination points at a Claude Code bug rather than your command.

Windows: Git Bash vs WSL bash on PATH#

The best-documented cause, and a regression introduced in v2.1.45:

#26505 — the Bash tool fails silently with exit code 1 when WSL's bash is on PATH alongside Git Bash. Claude Code resolves the wrong bash.exe, and the mismatch produces no output at all. #26545 reports the same on Git Bash since the same version.

Check which bash resolves first:

powershell
where.exe bash

If C:\Windows\System32\bash.exe (the WSL stub) comes before your Git Bash path, that's your problem. Point Claude Code at the right shell explicitly:

powershell
setx CLAUDE_CODE_GIT_BASH_PATH "C:\Program Files\Git\bin\bash.exe"

Open a new terminal afterwards — setx doesn't affect the session you ran it in.

Other reported exit-code-1 cases#

IssueSituation
#12545npm commands fail silently; work in a normal shell
#41124No stdout/stderr on CachyOS / Arch Linux
#22105Commands succeed but are reported as exit code 1
#19663No output on macOS 26
#12115Commands fail with exit code 1 and no output

#22105 is worth singling out: the command did the work and still reported failure. If your files changed but the tool says it failed, don't let the agent "fix" it — you'll get the operation applied twice.

The diagnostic#

Run the identical command in your own terminal:

bash
cd /your/project && the-exact-command-that-failed
echo "exit: $?"
  • Works in your shell, fails in Claude Code → a Claude Code bug; check the table above
  • Fails in both → your command; the exit code means what it normally means

Exit code 127: command not found#

The shell couldn't find the binary. Almost always PATH, and Claude Code inherits the environment of whatever launched it — which for a GUI is often not your shell's PATH.

bash
which the-command
echo $PATH

This is the same mechanism as 'codex' not found: a tool launched from a dock or Start menu never sourced your ~/.zshrc, so nvm, Homebrew, Volta, mise and custom npm prefixes are all invisible to it. That page has the per-OS fixes; substitute your binary name.

Exit code 137: killed#

128 + 9 — the process received SIGKILL. It didn't fail, it was killed. Two common reasons:

Out of memory. The kernel's OOM killer targets the largest process. Typical on builds, test suites, bundlers, and anything in a memory-capped container:

bash
# Linux — did the OOM killer fire?
dmesg | tail -30 | grep -i "killed process"

Docker containers hit this at their memory limit, not the host's.

An interrupt. #12153 reports claude auth commands failing with exit code 137 and [Request interrupted by user for tool use] in v2.0.50 — a regression rather than a memory problem. If you see 137 on a command that uses no memory at all, check your version:

bash
claude --version
npm i -g @anthropic-ai/claude-code@latest

Exit code 143: terminated#

128 + 15SIGTERM, a polite request to stop. Something asked the process to exit and it complied.

Usually a timeout: the Bash tool's own limit, a CI step limit, or an orchestrator stopping the container. Unlike 137, this is an orderly shutdown, so it more often means "took too long" than "something went wrong".

For long-running commands, prefer backgrounding them and polling over letting a foreground command sit until it's killed.

Reporting one#

Include the exit code, the exact command, whether it works in your own terminal, your OS and shell, and claude --version. For exit code 1 specifically, "works in my shell, fails in Claude Code with no output" is the single most useful sentence you can write — it separates a client bug from a command error immediately.

FAQ#

What does exit code 1 mean in Claude Code?#

Generically, that the command failed. But exit code 1 with no output, on a command that works in your own terminal, usually points at a Claude Code shell-resolution bug rather than your command — most commonly on Windows when WSL's bash is on PATH ahead of Git Bash.

Why does Claude Code return exit code 126 for a command that exists?#

Because Claude Code also uses 126 for a security refusal. Commands using eval, $() or backticks in command position are blocked regardless of allowlist, and the message says "permanent security restriction, not a transient error" — retrying cannot help. Rewrite so the binary is named literally.

What is exit code 137 in Claude Code?#

128 + 9, meaning the process was killed with SIGKILL. Usually the out-of-memory killer on a build or test run, or a container hitting its memory limit. It has also been reported as a v2.0.50 regression on claude auth commands, unrelated to memory.

What is the difference between exit code 137 and 143?#

137 is SIGKILL — the process was killed outright, typically by the OOM killer. 143 is SIGTERM — it was asked to stop and shut down cleanly, typically a timeout or cancellation.

Why does Claude Code report exit code 1 when my command actually worked?#

That's a reported bug (#22105). Check whether the work was actually done before letting the agent retry — otherwise the operation gets applied twice.

How do I fix exit code 1 on Windows with Git Bash?#

Run where.exe bash and check whether WSL's C:\Windows\System32\bash.exe resolves before Git Bash. If it does, point Claude Code at the Git Bash binary explicitly and open a new terminal, since the regression in v2.1.45 comes from resolving the wrong shell.