The Day claude Decided to Be Bun: A Windows Debugging Story
The Day claude Decided to Be Bun: A Windows Debugging Story
How a corrupted npm install led me down a PowerShell rabbit hole — and what it taught me about never trusting file paths blindly
We've all had that moment of quiet dread when a command you run every day suddenly does something completely unexpected. Mine happened on a regular Tuesday, in PowerShell, typing a command I'd typed a hundred times before.
claude
I expected Claude Code — Anthropic's AI coding assistant — to spin up in my terminal. Instead, this happened:
Bun is a fast JavaScript runtime, package manager, bundler, and test runner. (1.4.0+324c5f012)
Usage: bun <command> [...flags] [...args]
...
Bun. The JavaScript runtime. Not Claude Code. Somehow, my claude command had been hijacked.
This is the story of how I tracked it down — and the broader lesson it taught me about debugging on Windows.
The First Wrong Assumption
My first instinct was that something in my PATH was simply pointing to the wrong binary — a stale alias, maybe, or a leftover Bun shim. On Linux or macOS, this is a thirty-second fix: run which claude, see what comes back, fix the PATH order, done.
But I was on Windows. And Windows PowerShell doesn't have which.
which claude
which : The term 'which' is not recognized as the name of a cmdlet, function, script file, or operable program.
Right. First lesson of the day: PowerShell isn't bash, and pretending otherwise wastes time. The PowerShell-native equivalent is:
Get-Command claude | Select-Object -ExpandProperty Source
This returned:
C:\Users\pramo\AppData\Roaming\npm\claude.ps1
A .ps1 file. Not the .cmd file I expected.
The Precedence Trap
This is where things got interesting. When I checked the npm directory more broadly with where.exe claude, I found two files:
C:\Users\pramo\AppData\Roaming\npm\claude
C:\Users\pramo\AppData\Roaming\npm\claude.cmd
And inspecting claude.cmd showed it was perfectly correct — a clean Windows batch wrapper pointing straight at the real Claude Code executable:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
"%dp0%\node_modules\@anthropic-ai\claude-code\bin\claude.exe" %*
So why wasn't PowerShell using it?
Because PowerShell has its own command resolution order, and it differs from cmd.exe. When multiple executable types share a name in the same directory, PowerShell prioritizes them as:
.ps1 > .cmd > .exe
There was a third file I hadn't checked yet: claude.ps1. And that's the one PowerShell was actually running.
Reading the Shim
Get-Content C:\Users\pramo\AppData\Roaming\npm\claude.ps1
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
$exe=".exe"
}
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node_modules/@anthropic-ai/claude-code/bin/claude.exe" $args
} else {
& "$basedir/node_modules/@anthropic-ai/claude-code/bin/claude.exe" $args
}
exit $LASTEXITCODE
This script looked entirely correct. It pointed to exactly the right file: claude.exe inside the official @anthropic-ai/claude-code npm package. There was nothing wrong with the shim itself.
So the mystery deepened. If the shim was correct, and it pointed to the right file path — why was I still getting Bun?
Trusting the Path Less, Testing the Binary More
This was the turning point in the debugging process, and probably the most useful lesson in the whole story: a correct file path does not guarantee a correct file.
I confirmed the binary existed:
Test-Path "C:\Users\pramo\AppData\Roaming\npm\node_modules\@anthropic-ai\claude-code\bin\claude.exe"
True
Then I stopped trusting the path and actually executed the file directly, bypassing every shim and every layer of indirection:
& "C:\Users\pramo\AppData\Roaming\npm\node_modules\@anthropic-ai\claude-code\bin\claude.exe" --version
1.4.0
At first glance, this looked like a normal version number. But 1.4.0 was suspiciously familiar — it was the exact version string Bun had reported at the very start of this whole mess.
The file sitting at claude.exe, inside the official Claude Code npm package, inside the correctly-named folder, was not Claude Code at all. It was Bun.
Root Cause
The npm global installation of @anthropic-ai/claude-code had silently failed to install the correct native binary. Instead of downloading and placing Anthropic's actual claude.exe, something in the install process left a Bun binary in its place — most likely from a postinstall step that didn't complete as expected, possibly compounded by having Bun installed and active in the same environment.
The result: every layer of the system looked correct. Right PATH. Right shim. Right file path. Just the wrong contents at the very end of the chain.
The Fix (and a Reasonable Worry)
Before reinstalling anything, I had one important question: would this wipe out my Claude Code session history, memory, or configured agents?
It wouldn't. Claude Code stores all of that separately, in:
C:\Users\<you>\.claude\
The npm package only contains the executable. Session data, memory, and settings live entirely outside it. Reinstalling the package is safe.
So the fix was a clean reinstall:
npm uninstall -g @anthropic-ai/claude-code
npm install -g @anthropic-ai/claude-code
And verification:
claude --version
claude doctor
This time, claude --version returned an actual Claude Code version — not Bun's.
What This Taught Me About Debugging on Windows
A few transferable lessons came out of this, beyond just "reinstall the package":
1. PowerShell command resolution is not the same as bash. If you're debugging a "wrong binary runs" issue on Windows, check for .ps1, .cmd, and .exe files separately — they resolve in a specific priority order, and a correct .cmd sitting next to a broken .ps1 will lose every time.
2. Get-Command is more useful than where.exe for understanding what will actually run, since it respects PowerShell's resolution rules rather than just listing every match.
3. File existence is not file correctness. Test-Path returning True tells you a file is there. It tells you nothing about what's inside it. When debugging "wrong tool runs," always execute the binary directly and check its actual output — don't stop at confirming the path resolves.
4. Package managers can silently leave the wrong binary in place, especially in mixed environments running multiple JS runtimes (Node, npm, Bun) side by side. When in doubt, a clean uninstall/reinstall is often faster than chasing the root cause through postinstall scripts.
5. Separate your data from your binaries. Tools like Claude Code that store session/config data outside the package install directory make situations like this far less stressful — a broken binary doesn't mean lost work.
Closing Thought
It's a small bug, in the grand scheme of things — a single corrupted binary in a single global npm package. But it's a good reminder that in software, the path being correct and the thing at the path being correct are two entirely different claims. Verifying both, every time, is what separates a quick fix from an hour of confused guessing.
If you've hit something similar — claude, bun, npx, or any CLI tool resolving to the wrong binary on Windows — I'd genuinely like to hear about it in the comments. These cross-runtime conflicts seem to be getting more common as more of us run Node, Bun, and Deno side by side.
Debugged on Windows 11, PowerShell 7, with Bun 1.4.0 and Claude Code installed via npm.