Windows/Git Bash: shell snapshot replays 88 base64 evals per Bash tool call, ~2.3s fixed overhead on every command

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment · opened Jul 27, 2026

What happened?

On Windows with Git Bash, every Bash tool call pays a fixed ~2.3 s of pure fork overhead before the command runs. The cost is sourcing the shell snapshot, not spawning the shell and not the command itself.

This is the same mechanism as #31437 (macOS, closed as stale) and #19585 (Windows, closed as stale), but the Windows arithmetic is worse and the trigger needs no unusual user config — a stock Git for Windows install is enough.

Measurements (Git for Windows bash.exe, no EDR exclusions changed, machine freshly booted):

| step | time |
|---|---|
| bare bash -c exit | 53 ms |
| bash -c "source <snapshot>" (91 KB, 88 evals) | 2343 ms |
| same after reducing captured functions to 6 | 175 ms |

So spawn is healthy (53 ms) — this is not the MSYS2 spawn degradation in #80670. The entire delay is snapshot replay.

Why 88 functions on a stock install: Git for Windows ships /etc/profile.d/git-prompt.sh, which sources git-completion.bash. A login shell therefore defines 148 functions before ~/.bashrc is ever read:

$ bash -lc 'declare -F | wc -l'
148
$ bash -c 'declare -F | wc -l'
0

The snapshot's capture filter (grep -vE '^_[^_]') drops the _git_add-style names but keeps all 88 __git_* / __gitcomp_* ones. Each is replayed as:

eval "$(echo '<base64>' | base64 -d)" > /dev/null 2>&1

That is two forks per function. MSYS2 fork emulation costs ~27 ms/fork here, so 88 functions ≈ 2.3 s, paid on every Bash tool call for the entire session.

Confirmed the cost is fork count, not function content — 88 evals of a trivial f1 () { :; } body reproduce it exactly:

8 evals:  270 ms
88 evals: 2343 ms
bare spawn: 53 ms

These are all shell completion functions. They are dead weight in a non-interactive bash -c "<command>" context — no readline, no TAB, never invoked.

What did you expect to happen?

Bash tool calls should not carry a multi-second constant. Snapshot replay should not be O(forks) in the number of captured functions, and interactive-only completion functions pulled in by /etc/profile.d should not be captured at all.

Steps to reproduce

  1. Windows 11, stock Git for Windows (nothing added to ~/.bashrc).
  2. Start Claude Code, run any Bash tool command (echo hi).
  3. Observe ~2 s between the command appearing and it executing. Permission mode is irrelevant — reproduces under bypass, where no permission check runs.
  4. Confirm directly:
snap=$(ls -t ~/.claude/shell-snapshots/*.sh | head -1)
grep -c 'base64 -d' "$snap"          # 88
time bash -c "source $snap"          # ~2.3 s
time bash -c exit                    # ~0.05 s

Suggested fixes

  1. Don't fork per function. declare -f "$f" >> snapshot writes the definition directly; sourcing it costs zero forks. The base64+eval wrapper exists for fault isolation on bash 3.2 (macOS /bin/sh), which does not apply to Git Bash — bash 4+ can take the direct path. This alone turns 2.3 s into single-digit ms. (Same suggestion as #31437, which was closed without a fix.)
  2. Don't capture completion functions. They cannot be used by a non-interactive tool call. Filtering ^_*git-style completion names, or anything registered via complete -F, would cut the capture set to the handful of functions users actually define.

Workaround for others hitting this

Appending a non-interactive guard to ~/.bashrc (sourced after /etc/profile.d/*, so it can undo them) drops capture from 88 functions to 6 and snapshot cost from 2343 ms to 175 ms, while interactive Git Bash keeps its completions and __git_ps1 prompt:

case $- in
    *i*) ;;
    *) unset -f $(declare -F | awk '{print $3}' | grep '^_\+git') 2>/dev/null || true ;;
esac

Claude Code version

2.1.220

Operating system

Windows 11 (26200), Git for Windows bash, native claude.exe install

Related

  • #31437 — same mechanism, macOS, ~8 s/call, closed as stale
  • #19585 — Windows, 30-90 s/call, closed as stale
  • #80670 — Windows Bash slowness from MSYS2 spawn degradation; different cause (spawn was 53 ms here)

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗