anomalyco / anomalyco/opencode
bash tool: timeout kills the shell but does not abandon the stdio read — call duration is unbounded
@neriousy is already working on this.
Since Sep 15, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.