Skip to content

bash tool: timeout kills the shell but does not abandon the stdio read — call duration is unbounded #49169

Closed as not planned

Description

@achimismaili

Description

Summary

The bash tool's timeout terminates the shell process on schedule, but then keeps reading its
stdout/stderr pipe. If the command spawned a process that inherited those handles, EOF never arrives
while that process lives — so the call blocks well past the timeout, unbounded for a long-lived server.

The returned message is also misleading: it says the command was terminated at the timeout, when in fact
substantially more wall-clock time elapsed before the call returned.

  • Bounded, reproducible: timeout: 5000 → call returned after 25.79 s
  • Observed in practice: timeout: 120000 → call returned after 1842.5 s (30.7 min), and only
    because the process tree was killed externally

Suggested fix

On timeout, stop awaiting stream EOF: resolve from process exit and drain stdio with a deadline, so
timeout is a hard upper bound on the call regardless of stream state. Optionally also kill the
process group / job object so descendants don't survive the timeout — they currently do, and are
never reaped.

Plugins

OMO

OpenCode version

1.18.30

Steps to reproduce

Part A — the mechanism (standalone, no opencode needed, ~25 s)

Paste into PowerShell. It appears to freeze for ~25 s; that freeze is the bug, and it self-terminates.

$inner = Join-Path $env:TEMP 'pipedemo-inner.ps1'
@'
Start-Process -FilePath "powershell.exe" `
  -ArgumentList @("-NoProfile", "-Command", "Start-Sleep -Seconds 25") `
  -WindowStyle Hidden `
  -RedirectStandardOutput "$env:TEMP\hang-o.log" `
  -RedirectStandardError "$env:TEMP\hang-e.log" | Out-Null
"INNER_SCRIPT_DONE at $(Get-Date -Format o)"
'@ | Set-Content -LiteralPath $inner -Encoding utf8

$sw  = [System.Diagnostics.Stopwatch]::StartNew()
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName  = 'powershell.exe'
$psi.Arguments = "-NoProfile -File `"$inner`""
$psi.RedirectStandardOutput = $true
$psi.UseShellExecute = $false
$p = [System.Diagnostics.Process]::Start($psi)
$null = $p.WaitForExit(60000); $exitedAt = $sw.Elapsed.TotalSeconds
$out  = $p.StandardOutput.ReadToEnd(); $eofAt = $sw.Elapsed.TotalSeconds
"child process EXITED: $([math]::Round($exitedAt,2)) s"
"child stdout EOF    : $([math]::Round($eofAt,2)) s"
Remove-Item -LiteralPath $inner -Force -ErrorAction SilentlyContinue

Measured (Windows 11 build 26200, PowerShell 7.6.6):

child process EXITED: 0.26 s
child stdout EOF    : 25.41 s

25.15 s of blocking after the child had already exited. Running the inner script directly in a
console returns in 0.58 s — the hang only appears when stdout is a pipe being read to EOF, which is
why it never reproduces interactively.

Part B — the timeout does not bound the call (through the bash tool, ~25 s)

Invoke the bash tool with timeout: 5000 and this command:

$inner = Join-Path $env:TEMP 'to-test.ps1'
@'
Start-Process -FilePath "powershell.exe" -ArgumentList @("-NoProfile","-Command","Start-Sleep -Seconds 25") -WindowStyle Hidden -RedirectStandardOutput "$env:TEMP\to-o.log" -RedirectStandardError "$env:TEMP\to-e.log" | Out-Null
"CHILD_SPAWNED"
'@ | Set-Content -LiteralPath $inner -Encoding utf8
& powershell.exe -NoProfile -File $inner
"script finished at $(Get-Date -Format o)"

Expected: the call returns at ~5 s.
Actual: it returns at 25.79 s (recorded state.time.end - state.time.start), i.e. when the
detached child exits and EOF finally arrives — while reporting:

shell tool terminated command after exceeding timeout 5000 ms

The timeout did fire and did kill the shell; it simply did not stop the read. With a server in place
of Start-Sleep 25, the call never returns at all.

Screenshot and/or share link

No response

Operating System

Windows 11 Pro build 26200, AMD64

Terminal

PowerShell 7.6.6

Activity

  1. github-actions commented on Sep 15, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Your report adds the specific observation that the timeout parameter does not bound the call duration (the timeout fires and kills the shell, but the read continues), and includes a reproducible measurement showing 25.79 s elapsed despite timeout: 5000. That aspect — timeout not bounding call duration — overlaps most closely with #47350's point 2 ("The wait seems to have no timeout") and the closed issues above.

    If those issues don't fully cover the timeout-not-bounding-the-call angle, it may be worth cross-referencing rather than reopening.

  2. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions