[BUG] "fg: no job control" hook_non_blocking_error regression on Windows (sync hooks), reappears after #4481

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

Environment

  • Claude CLI version: 2.1.216 (Claude Code)
  • Operating System: Windows 11 Pro (build 10.0.26200)
  • Terminal: Git Bash (GNU bash, version 5.3.15(1)-release (x86_64-pc-cygwin))
  • CLAUDE_CODE_GIT_BASH_PATH: C:\Program Files\Git\bin\bash.exe

Bug Description

fg: no job control reappears as a recurring, non-blocking hook error on Windows, closely matching the symptom originally reported in #4481 (which was closed as fixed in v1.0.77). It reproduces consistently on 2.1.216 for synchronous (non-async) "type": "command" hooks — PreToolUse, non-async PostToolUse, Stop, SessionStart, SessionEnd, Notification, PermissionRequest.

Across a single long session's transcript (~1600 lines), I found:

  • 133 occurrences of stderr: "/usr/bin/bash: line 1: fg: no job control", "type": "hook_non_blocking_error", spread across PreToolUse:Bash, PreToolUse:Read, PreToolUse:Write, PreToolUse:WebFetch, PreToolUse:WebSearch, PreToolUse:Grep, PreToolUse:Edit, PostToolUse:Read, Stop, SessionStart:resume, SessionStart:compact.
  • 0 occurrences of this error for hooks marked "async": true in the same session (e.g. three PostToolUse hooks on Edit|Write that run auto-backup scripts), despite running many times successfully.

This strongly suggests the synchronous hook-execution path spawns/waits on the configured git-bash process in a way that invokes job control (fg) on a non-interactive shell that never enabled it, while the async path does not.

A related, likely-connected symptom in the same transcript: hooks whose "command" starts with bash.exe directly (e.g. C:/PROGRA~1/Git/bin/bash.exe %USERPROFILE%/.claude/hooks/foo.sh) intermittently failed with Failed with non-blocking status code: /usr/bin/bash: %USERPROFILE%/.claude/hooks/foo.sh: No such file or directory (29 occurrences) — i.e. the Windows-style %USERPROFILE% env-var was not expanded before reaching bash, only for synchronous hooks (the equivalent async: true bash.exe-invoked hooks in the same settings.json never showed this). Both symptoms only affect the synchronous/blocking hook path.

Steps to Reproduce

  1. On Windows, set CLAUDE_CODE_GIT_BASH_PATH to a Git for Windows bash.exe.
  2. Configure any synchronous (no "async": true) hook of type "command" in ~/.claude/settings.json, e.g. under PreToolUse, Stop, or SessionStart.
  3. Trigger the matching event (e.g. run any Bash tool call, or end a turn for Stop).
  4. Observe hook_non_blocking_error with stderr: "/usr/bin/bash: line 1: fg: no job control" attached to that tool call / turn.

Actual Behavior

The hook's own logic still appears to execute and return the correct result (verified independently by invoking the underlying scripts directly with the same JSON payload — they behave correctly), so this is presented by Claude Code as non-blocking. However it is very noisy: it shows up on nearly every synchronous hook invocation, which across a long session becomes hundreds of spurious error entries in the transcript/UI.

Additional Context

Workaround found for the %USERPROFILE%/"No such file" half of the symptom: switching the hook's "command" from
C:/PROGRA~1/Git/bin/bash.exe %USERPROFILE%/.claude/hooks/foo.sh
to
C:/PROGRA~1/Git/bin/bash.exe -c "$HOME/.claude/hooks/foo.sh"
(letting bash itself expand $HOME instead of relying on external %VAR% substitution) eliminated the "No such file" failures. No workaround found for the fg: no job control message itself — it appears to originate from Claude Code's own synchronous hook-invocation wrapper around git-bash, not from anything in the hook script or its command string.

Referencing #4481 since it's the same exact error message and platform, previously closed as fixed for an older 1.0.x version, but reproducing again on 2.1.216 through a different trigger (any synchronous hook, not just "any command" in general).

View original on GitHub ↗

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