Bash tool returns "internal error" on git commit under fish + multi-step pre-commit hooks (cmd actually succeeds)

Resolved 💬 2 comments Opened May 26, 2026 by wuxianzhong-gpt Closed Jun 27, 2026

Summary

Bash tool returns "internal error" (and the message [Tool result missing due to internal error]) when running git commit against a repo with multi-step pre-commit hooks under fish shell, even though the underlying git commit actually succeeds. The error reproduces consistently across 6+ attempts in the same session, and only on git commit — other fish commands (uv run, ruff, pytest, git status, git log, git push) work fine.

Environment

  • Claude Code: latest (Opus 4.7 / 1M context, fast mode on)
  • OS: macOS Darwin 25.5.0 (arm64)
  • Shell: fish 3.x (/opt/homebrew/bin/fish)
  • Repo has a custom hooks path (core.hooksPath=scripts/git-hooks) running a chained pre-commit:
  • musubito-check (LLM judge, ~5s)
  • ruff check
  • ruff format --check
  • mypy --strict (slowest; ~30-90s on touched src files)
  • Hooks output uses ANSI color escape codes (\x1b[36m, \x1b[31m) on stderr/stdout

Reproduction

  1. Run any git commit -F /tmp/msg.txt (or -m "...") via Bash tool in fish shell where pre-commit takes >30s and prints ANSI-colored multi-line stderr.
  2. Tool returns [Tool result missing due to internal error] — no exit code, no stdout/stderr surfaced.
  3. git log --oneline -1 afterward confirms the commit actually landed.

Variants that also repro the internal error:

  • git commit -F /tmp/msg.txt > /tmp/out.log 2>&1; echo "EXIT=$?" (redirect)
  • git commit -F /tmp/msg.txt --no-verify (skips hooks but still hits Bash tool path)
  • With explicit timeout: 300000 parameter (so it's not a 2-min timeout issue)

Workaround that works

Wrapping in bash -c:

bash -c "git commit -F /tmp/msg.txt"

This consistently succeeds and the tool returns normal output. Same workaround works for git push and gh pr create when their output is long.

Hypothesis

Tool framework's stream parser appears to choke on the intersection of:

  1. fish shell (non-POSIX stdout/stderr buffer flushing semantics)
  2. ANSI color escape sequences in stderr
  3. Command execution time ≥ ~1s (or any specific blocking I/O pattern from git commit spawning subprocesses)

Each factor alone is fine — only the combination breaks. bash -c wrap bypasses #1, which is enough.

Impact

For sessions involving any non-trivial commit (project with pre-commit framework, lint/format/mypy hooks), the agent loses 5-10 minutes per commit attempting blind retries before manually wrapping in bash -c. User-facing: agent appears stuck or broken; trust erosion.

Suggested fix direction

  • Robust stream reader for fish-launched subprocess (force LINES=200 COLUMNS=200 TERM=dumb or similar to strip ANSI)
  • Or always wrap user Bash commands in bash -c "..." when shell is fish (transparent normalization)
  • At minimum, surface the actual error condition instead of generic "internal error" so users/agents can diagnose

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗