Skip to content

Conversation

dnouri
Copy link
Contributor

@dnouri dnouri commented Jul 23, 2025

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).

dnouri added 2 commits July 25, 2025 11:14
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

- 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
@dnouri dnouri force-pushed the bash-tool-background branch from 811e7f3 to 46b7c23 Compare July 25, 2025 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant