zsh shell snapshot drops single-underscore helper functions and causes gvm startup noise
Summary
Claude Code on zsh can emit startup noise when using shell snapshots with environments that source gvm.
Impact
When Claude restores a zsh shell from a generated snapshot, the snapshot can retain gvm functions like setValueForKeyFakeAssocArray, valueForKeyFakeAssocArray, __gvm_read_environment_file, and cd, but omit their required single-underscore helpers _encode and _decode. This leaves the restored shell in a partially-defined state and can produce command-not-found noise during shell initialization.
Environment
- Claude Code session shell:
/bin/zsh - Repo:
Flynnon/remote_claude - Observed snapshot file:
~/.claude/shell-snapshots/snapshot-zsh-1774763716489-ft5eg9.sh - User shell init sources gvm from
~/.zshrc
Symptoms
The noisy startup was traced to gvm's fake associative-array helpers. The shell reports missing helper functions during startup / directory-change hooks, e.g.:
setValueForKeyFakeAssocArray:27: command not found: _encode
valueForKeyFakeAssocArray:28: command not found: _decode
Root Cause Analysis
This is not caused by .zshrc itself, and it is not caused by gvm failing under a normal interactive zsh startup.
The failure only appears in Claude's restored shell snapshot state:
~/.zshrcsources gvm.- gvm overrides
cd()and uses__gvm_read_environment_file(). __gvm_read_environment_file()callssetValueForKeyFakeAssocArray()/valueForKeyFakeAssocArray().- Those functions depend on
_encode()/_decode()from~/.gvm/scripts/function/_bash_pseudo_hash. - The generated Claude snapshot contains the higher-level functions but omits
_encode()/_decode(). - After restore, the shell is inconsistent: callers exist, helper functions do not.
Evidence from the snapshot:
- Present in snapshot:
setValueForKeyFakeAssocArray ()valueForKeyFakeAssocArray ()__gvm_read_environment_file ()cd ()- Missing from snapshot:
_encode ()_decode ()
Additional signal: the snapshot preserved many __double_underscore functions, but effectively filtered out single_underscore helpers. In the affected snapshot there were 17 __... functions but only 1 _... function, while gvm's source helper file defines both _encode and _decode alongside the exported wrappers.
Reproduction
A minimal repro shape is:
- Use zsh as Claude Code's shell.
- Source gvm from
~/.zshrc. - Let Claude create and reuse a shell snapshot.
- Restore from that snapshot.
- Observe missing
_encode/_decodewhile gvm wrapper functions are still present.
You can inspect the affected snapshot directly and compare it with gvm source:
python3 - <<'PY2'
from pathlib import Path
snap = Path.home()/'.claude/shell-snapshots/snapshot-zsh-1774763716489-ft5eg9.sh'
text = snap.read_text(encoding='utf-8', errors='ignore')
for needle in [
'setValueForKeyFakeAssocArray ()',
'valueForKeyFakeAssocArray ()',
'__gvm_read_environment_file ()',
'cd ()',
'_encode ()',
'_decode ()',
]:
print(needle, needle in text)
PY2
Expected:
- If a function's callers are snapshotted, its helper functions should also be present.
Actual:
- Wrapper/caller functions are present, but
_encode/_decodeare missing.
Why this matters
The snapshot mechanism appears to be serializing only part of the shell function graph. Filtering out single-underscore helpers breaks valid shell setups that depend on private helper names.
Suggested fix direction
Please review the shell snapshot generation / restore logic for function-name filtering. In particular, verify whether functions beginning with a single underscore are being dropped intentionally. If so, that filter is too aggressive: private helper functions can still be required by restored public or semi-public functions.
A safe invariant would be:
- if a function is persisted into the snapshot, any functions it references that are defined in the same captured shell state should not be silently dropped based on naming convention alone.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗