Shell snapshot captures builtin wrappers while dropping the _-prefixed helpers they call — cd exits 127 without changing directory (v2.1.220, 6th report since Jan)
Preflight Checklist
- [x] I have searched existing issues
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
Summary
The shell snapshot builder captures functions that wrap shell builtins while dropping the _/__-prefixed helpers those wrappers call, producing a shell in which cd is guaranteed to fail. This has been reported at least six times since January 2026, closed as COMPLETED at least three times, and closed as NOT_PLANNED at the root-cause level once. It reproduces on v2.1.220 today.
I am filing a new issue only because the prior threads are locked (7-day auto-lock after closure), so "still broken" cannot be added to them.
Environment
| | |
|---|---|
| Claude Code | 2.1.220 |
| mise | 2026.6.14 linux-x64 (2026-06-25) |
| bash | GNU bash 5.1.16(1)-release (x86_64-pc-linux-gnu) |
| OS | Linux 6.8.0-124-generic |
What's Wrong?
With mise activated (eval "$(mise activate bash)" in ~/.bashrc), every Bash tool call that invokes cd emits:
/home/me/.claude/shell-snapshots/snapshot-bash-1785160735574-zxu151.sh: line 10: __zsh_like_cd: command not found
mise activate bash wraps cd/pushd/popd to call the helper __zsh_like_cd, which is how mise detects directory changes. The snapshot captures the wrapper and drops the helper.
Evidence from the live snapshot
The snapshot stores functions as base64-encoded eval payloads. Decoding every payload in my snapshot yields 4 captured functions total:
cd climb fname Presenter
The cd entry decodes to:
cd ()
{
__zsh_like_cd cd "$@"
}
__zsh_like_cd appears nowhere in the snapshot — not as plaintext, not inside any base64 payload.
For scale: my interactive shell defines 158 functions, 111 of which are _-prefixed (26 of those __-prefixed). The snapshot captured 4, and one of those four contain a call to one of the 111 it discarded.
So the capture filter kept a function whose only body is a call to a function the same filter deleted. The result is not a degraded shell — it is a shell where cd cannot succeed, by construction.
Why this is a correctness/safety issue, not just stderr noise
The broken cd fails without changing the working directory, so any subsequent command in the same invocation runs somewhere unintended:
$ bash -c "source <snapshot>; cd /tmp; echo \"cd exit=\$?; pwd now: \$(pwd)\""
<snapshot>: line 10: __zsh_like_cd: command not found
cd exit=127; pwd now: /home/me
cd /tmp reported failure and left the shell in /home/me. In a compound command, && short-circuits and is safe — but cd /project/build; rm -rf ./* executes the deletion in the directory the agent started in, which for Claude Code is the user's working directory. Neither the model nor the user can tell from the command text that cd is a broken function rather than a builtin, and the agent has no way to detect that its own cd is non-functional.
This is the same class as #80103 ("Bundled search wrappers narrow silently and exit 0"): an injected wrapper that changes semantics while looking like a primitive.
Prior reports and their dispositions
| issue | filed | title | closed as |
|---|---|---|---|
| #20464 | 2026-01-23 | Shell snapshot breaks zoxide cd wrapper — __zoxide_z not found | NOT_PLANNED |
| #21939 | 2026-01-30 | Shell snapshot doesn't capture zoxide dependency functions | DUPLICATE (same day) |
| #23091 | 2026-02-04 | Shell snapshots filter out __-prefixed functions, breaking dependent functions (root cause) | NOT_PLANNED |
| #25824 | 2026-02-14 | Shell snapshot drops __-prefixed functions, breaking cd when used with mise | COMPLETED |
| #40602 | 2026-03-29 | zsh snapshot drops single-underscore helpers, gvm noise | COMPLETED (same day) |
| #55816 | 2026-05-03 | still drops underscore-prefixed helpers in v2.1.112 (recurrence of #40602) | COMPLETED (same day) |
| #55985 | 2026-05-04 | filters _-prefixed functions, breaking GVM _encode/_decode | DUPLICATE |
| #60397 | 2026-05-19 | omits single-underscore functions, breaking scm_breeze | CLOSED |
Two things worth flagging about this history:
- #25824 was closed as
COMPLETEDwith zero human comments. The only activity was agithub-actionsbot announcing auto-closure as a duplicate of #23091, #20464, and #21939 — two of which were themselves closedNOT_PLANNED, and oneDUPLICATE. A specific bug was marked fixed by pointing at a general bug marked won't fix.
- #55816 exists solely to report that #40602's fix did not work, and was itself closed
COMPLETEDon the day it was filed. #55985 and #60397 were then filed after that.
The 7-day auto-lock means each recurrence must become a new issue, which the duplicate-detector then closes against the locked originals. That loop has no exit, which is why this is now in its sixth month.
Expected Behavior
Any of the following would fix it; the first is the only one that is robust:
- Capture the transitive closure. If a captured function's body references another function, capture that one too, regardless of name. The filter is presumably there to skip completion helpers — dependency-following is compatible with that goal.
- Drop dependents along with dependencies. If a helper is filtered out, do not capture functions that call it. A missing
cdwrapper falls back to the builtin, which is correct behavior. A present wrapper calling a missing helper is never correct. - Never snapshot wrappers around builtins (
cd,pushd,popd,command,source), since the builtin fallback is always safe.
At minimum, the snapshot builder should validate the environment it produces — sourcing the snapshot and checking that every referenced function resolves would have caught all eight reports above at build time.
Steps to Reproduce
- Install
mise; addeval "$(mise activate bash)"to~/.bashrc - Open a terminal so mise activates
- Launch
claude - Run any command containing
cd, e.g.cd /tmp && ls - Observe
__zsh_like_cd: command not found
Reproduces without mise on any setup where a captured function calls an _-prefixed one (zoxide, gvm, scm_breeze — see table).
Is this a regression?
Yes — #55816 documents the same filter regressing after a prior fix. It has never been fixed at the root-cause level (#23091, NOT_PLANNED).
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗