#23309: fix: remove 30-minute timeout for background exec sessions
agents
size: XS
Cluster:
Cron Job Stability Fixes
## Problem
Background exec sessions (started with `background: true` or `yieldMs`) are silently killed after exactly 30 minutes. Users experience this as mysterious `Exec failed (session-id, signal SIGTERM)` messages with no clear cause.
**Real-world impact:** Any background task running longer than 30 minutes is killed, including:
- Batch audio transcription (Whisper processing 930+ files)
- Continuous monitoring/polling loops (IoT device tracking, Tesla location polling)
- Long-running data processing pipelines
Users must resort to `nohup` workarounds, which lose all `process` tool integration (log, poll, kill).
Fixes #23303
## Root Cause
The `defaultTimeoutSec = 1800` (30 minutes) in `createExecTool` is applied to **all** exec sessions, including background ones. This timeout is passed to the process supervisor, which fires an `overall-timeout` cancellation via `adapter.kill("SIGKILL")` after 30 minutes, regardless of whether the session was explicitly backgrounded.
This is distinct from context compaction (which is often what users are doing when they notice the kills), though compaction can also terminate sessions through separate mechanisms.
## Fix
**2 files changed, 16 insertions, 4 deletions** — minimal and surgical.
### `src/agents/bash-tools.exec.ts`
- When a session will run in the background (`background: true` or `yieldMs`) AND no explicit `timeout` parameter was provided: skip the supervisor timeout entirely (`null`)
- When an explicit `timeout` is provided: always respect it (unchanged)
- For synchronous foreground exec: keep the 30-minute safety default (unchanged)
### `src/agents/bash-tools.exec-runtime.ts`
- Widen `timeoutSec` type from `number` to `number | null` to support no-timeout background sessions
- The existing `typeof opts.timeoutSec === "number" && opts.timeoutSec > 0` guard already handles `null` correctly (produces `timeoutMs = undefined`, so the supervisor never sets a kill timer)
- Guard the timeout error message against `null`
## Behavior Matrix
| Scenario | Before | After |
|----------|--------|-------|
| Foreground exec, no timeout | 30 min timeout ✓ | 30 min timeout ✓ (unchanged) |
| Foreground exec, explicit timeout | User timeout ✓ | User timeout ✓ (unchanged) |
| Background exec, no timeout | **30 min timeout (bug)** | **No timeout ✓ (fixed)** |
| Background exec, explicit timeout | User timeout ✓ | User timeout ✓ (unchanged) |
## Related Issues
- #23303 — Issue report with detailed reproduction steps
- #20436 — Session context lost after agent run timeout (related lifecycle issue)
- #18237 — Feature Request: Async exec callback (related long-running process management)
## Testing
This was discovered and verified through real-world usage:
1. Launched Whisper batch transcription (930 audio files, ~20 min job) — killed at exactly 30 min, 3 separate times
2. Launched Tesla location polling loop (every 5 min) — killed at exactly 30 min, repeatedly
3. After applying this fix concept (using `nohup` to bypass the supervisor timeout), both processes completed successfully
The fix preserves all existing timeout behavior for foreground sessions and explicit user timeouts.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes a critical bug where background exec sessions were silently killed after 30 minutes due to a hardcoded default timeout. The fix correctly distinguishes between foreground and background sessions, removing the timeout for background sessions unless explicitly set by the user.
**Key changes:**
- `bash-tools.exec.ts`: Updated timeout logic to skip the supervisor timeout (`null`) for background sessions without an explicit user-provided timeout
- `bash-tools.exec-runtime.ts`: Widened `timeoutSec` type from `number` to `number | null` and added null guard for timeout error message
**Analysis:**
The logic correctly handles all cases:
- Foreground sessions maintain the 30-minute safety default
- Background sessions (via `background: true` or `yieldMs`) run without timeout when no explicit timeout is provided
- Explicit user timeouts are always respected regardless of session type
- When backgrounding is disabled (`allowBackground: false`), the default timeout still applies correctly
The existing guard `typeof opts.timeoutSec === "number" && opts.timeoutSec > 0` already handles `null` correctly by producing `timeoutMs = undefined`, preventing the supervisor from setting a kill timer.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge - it's a minimal, surgical fix that resolves a critical bug without introducing new risks
- The change is well-scoped (2 files, 16 insertions, 4 deletions), preserves all existing behavior for foreground sessions and explicit timeouts, and the logic correctly handles all edge cases. Existing tests already cover background sessions with and without explicit timeouts, confirming the fix works as intended.
- No files require special attention
<sub>Last reviewed commit: 0e10f53</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#18432: fix(agents): clear active run state immediately on embedded timeout
by BinHPdev · 2026-02-16
77.9%
#15378: feat(exec): support `background: false` to force synchronous execution
by TorbenWetter · 2026-02-13
77.1%
#19414: fix: respect job timeoutSeconds for stuck runningAtMs detection
by namabile · 2026-02-17
76.1%
#18193: fix: default elevatedDefault to 'off' instead of 'on' (#18177)
by lailoo · 2026-02-16
75.6%
#8464: feat: make exec approval timeout configurable
by fabioaraujopt · 2026-02-04
75.4%
#21661: fix(agents): treat approval timeout as denial regardless of askFall...
by AI-Reviewer-QS · 2026-02-20
75.3%
#15050: fix: transcript corruption resilience — strip aborted tool_use bloc...
by yashchitneni · 2026-02-12
75.2%
#22719: fix(agents): make subagent announce timeout configurable (restore 6...
by Valadon · 2026-02-21
75.2%
#9114: Fix: Reduce NO_TIMEOUT_MS to 24 days to avoid Node.js overflow warning
by vishaltandale00 · 2026-02-04
75.1%
#12477: fix(agents): prevent TimeoutOverflowWarning when timeout is disabled
by skylarkoo7 · 2026-02-09
74.8%