Bash timeout auto-backgrounding trips internal-deny bug (same class as #27203/#37442/#40241, different trigger)
Bug description
A Bash tool call that exceeds its execution timeout and gets auto-promoted to background execution (the CLI's own timeout-driven backgrounding, not an explicit run_in_background: true) can trip the same internal-deny bug already reported in #27203, #37442, and #40241 — but through a trigger those issues don't cover.
Those issues document: explicit background Task/Agent subagents bypass the permission path entirely and get internally denied, corrupting the transport and cascading denials to sibling/main tool calls. This report shows the identical failure signature (is_error:true, toolDenialKind:"user-rejected", text "The user doesn't want to proceed with this tool use... STOP what you are doing and wait for the user to tell you how to proceed.") triggered by an ordinary Bash call that started in the foreground and was auto-backgrounded by the timeout watchdog — with permission_mode: bypassPermissions active and zero human present (a fully headless claude -p --resume session via the Python Agent SDK).
Steps to reproduce
- Run a headless session via the Python
claude-agent-sdk(orclaude -p --output-format stream-json --permission-mode bypassPermissions). - Have the model run 2+ Bash commands that each exceed the default 120s execution timeout — each gets cleanly auto-backgrounded ("Command did not complete within its 120s timeout and was moved to the background (ID: ...)").
- Have the model run a 3rd such command (in our case, one that additionally hangs waiting on a macOS
osascript/System Events GUI permission dialog nobody can answer — likely relevant, see below). - Instead of a 3rd clean background-promotion, the tool call comes back denied:
is_error:true,toolDenialKind:"user-rejected", timestamped exactly ~120s after the call (i.e. it's the timeout firing, not a real rejection). - The corruption survives
--resume: a later turn that resumes the sameclaude_session_idgets its very first tool call denied the same way, in 16ms — far too fast to be a timeout, indicating the poisoned transport/permission state persists across resume rather than resetting.
Expected behavior
A timed-out Bash call should either background cleanly (as the first two did) or return a normal timeout error — never a synthetic "user rejected" denial with no human involved, and never corrupt state that survives into a resumed session.
Workaround found
Setting CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 prevents the auto-backgrounding path entirely — a timed-out Bash call instead returns a clean Exit code 143 / Command timed out after Ns, with no cascading denial observed. Verified directly against CLI 2.1.207 with a synthetic repro (Bash call given an explicit 3000ms timeout on an 8s sleep).
This works as a mitigation for us since our use case (a single-shot orchestration wrapper) never relies on background survival anyway — but it's a workaround, not a fix: it doesn't address whatever internal state corruption is actually happening when timeout-triggered backgrounding is enabled, and per #27203/#37442/#40241 the same class of corruption is reachable other ways too.
Environment
- Claude Code CLI: 2.1.207 (also observed on 2.1.212)
- claude-agent-sdk (Python)
- macOS
- Invocation: headless,
permission_mode="bypassPermissions", via the Python SDK'squery()(subprocess-wrapped)
Related issues
- #27203, #37442, #40241 — same failure signature, different (explicit background subagent) trigger
---
_Drafted by Claude Code (Claude Sonnet 5), filed by @tjsimonX._