[PR #1254] [CLOSED] fix: resolve BashTool hanging on background commands #9863

Closed
opened 2026-02-16 18:14:17 -05:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opencode/pull/1254
Author: @dnouri
Created: 7/23/2025
Status: Closed

Base: devHead: bash-tool-background


📝 Commits (2)

  • 0366150 fix: resolve BashTool hanging on background commands
  • 46b7c23 Update tests

📊 Changes

4 files changed (+1046 additions, -4 deletions)

View changed files

📝 packages/opencode/src/tool/bash.ts (+95 -4)
packages/opencode/test/integration/background-commands.test.ts (+332 -0)
packages/opencode/test/tool/bash-background.test.ts (+230 -0)
packages/opencode/test/tool/bash-backgrounding-methods.test.ts (+389 -0)

📄 Description

BashTool hung indefinitely on background commands (sleep 5 &, node server.js &) due to file descriptor inheritance. await process.exited blocked because background processes inherited stdout/stderr pipes from the parent bash shell.

  1. Bun.spawn() creates bash with stdout: "pipe", stderr: "pipe"
  2. Bash executes command & and exits
  3. Background process inherits pipe descriptors
  4. process.exited waits for all descriptors to close
  5. Background process keeps descriptors open → infinite hang

Added background command detection with selective I/O redirection:

const isBackground = isBackgroundCommand(params.command)
stdout: isBackground ? "ignore" : "pipe",
stderr: isBackground ? "ignore" : "pipe",

Background detection handles:

  • Simple: sleep 5 &
  • Complex: (cmd1; cmd2) &, nohup cmd &, cmd & disown
  • Edge cases: quoted strings, escaped ampersands
  • Mixed commands treated as foreground to preserve output

Now:

  • Background commands return quickly (vs infinite hang)
  • 35 comprehensive tests covering unit, integration, and edge cases
  • Full backward compatibility maintained
  • No performance impact on foreground commands

⚠️ One weakness here is that the new isBackgroundCommand function is 83 lines long and tries to handle a lot of cases. This might be a bit much for an issue that doesn't seem to occur for a lot of developers (haven't seen an open issue about this). At the same time, this PR might be a segway to fixing other issues with BashTool I/O as well such as #652 (App becomes unresponsive when executing commands requiring sudo password).


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/anomalyco/opencode/pull/1254 **Author:** [@dnouri](https://github.com/dnouri) **Created:** 7/23/2025 **Status:** ❌ Closed **Base:** `dev` ← **Head:** `bash-tool-background` --- ### 📝 Commits (2) - [`0366150`](https://github.com/anomalyco/opencode/commit/036615029e85ac983a97f8769308c2a4f72bcd32) fix: resolve BashTool hanging on background commands - [`46b7c23`](https://github.com/anomalyco/opencode/commit/46b7c2327792fe967fa3beb2b44e3c97285a6309) Update tests ### 📊 Changes **4 files changed** (+1046 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `packages/opencode/src/tool/bash.ts` (+95 -4) ➕ `packages/opencode/test/integration/background-commands.test.ts` (+332 -0) ➕ `packages/opencode/test/tool/bash-background.test.ts` (+230 -0) ➕ `packages/opencode/test/tool/bash-backgrounding-methods.test.ts` (+389 -0) </details> ### 📄 Description BashTool hung indefinitely on background commands (`sleep 5 &`, `node server.js &`) due to file descriptor inheritance. `await process.exited` blocked because background processes inherited stdout/stderr pipes from the parent bash shell. 1. `Bun.spawn()` creates bash with `stdout: "pipe", stderr: "pipe"` 2. Bash executes `command &` and exits 3. Background process inherits pipe descriptors 4. `process.exited` waits for all descriptors to close 5. Background process keeps descriptors open → infinite hang Added background command detection with selective I/O redirection: ```typescript const isBackground = isBackgroundCommand(params.command) stdout: isBackground ? "ignore" : "pipe", stderr: isBackground ? "ignore" : "pipe", ``` Background detection handles: - Simple: `sleep 5 &` - Complex: `(cmd1; cmd2) &`, `nohup cmd &`, `cmd & disown` - Edge cases: quoted strings, escaped ampersands - Mixed commands treated as foreground to preserve output Now: - Background commands return quickly (vs infinite hang) - 35 comprehensive tests covering unit, integration, and edge cases - Full backward compatibility maintained - No performance impact on foreground commands ⚠️ One weakness here is that the new `isBackgroundCommand` function is 83 lines long and tries to handle a lot of cases. This might be a bit much for an issue that doesn't seem to occur for a lot of developers (haven't seen an open issue about this). At the same time, this PR might be a segway to fixing other issues with BashTool I/O as well such as #652 (App becomes unresponsive when executing commands requiring sudo password). --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-02-16 18:14:17 -05:00
yindo closed this issue 2026-02-16 18:14:17 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#9863