Skip to content

[Bug] Shell timeout kill chain deadlocks: handle.kill() never resolves, tool part stays running forever #38291

Closed

Description

@woohahahaaa

Description

Shell tool timeout fires but the kill + exit-code + tool-state finalization chain deadlocks, leaving the tool part permanently running and blocking the parent session indefinitely.

Root cause analysis

After cloning the source and inspecting packages/opencode/src/tool/shell.ts and packages/core/src/cross-spawn-spawner.ts, the issue is in the timeout handling chain:

  1. Shell commands on macOS are spawned with detached: true, creating a new process group.
  2. When timeout fires, OpenCode calls handle.kill({ forceKillAfter: "3 seconds" }):
    if (exit.kind === "timeout") {
      expired = true
      yield* handle.kill({ forceKillAfter: "3 seconds" }).pipe(Effect.orDie)
    }
  3. handle.kill() internally does:
    • process.kill(-pid, "SIGTERM") (kill the process group)
    • Effect.timeoutOrElse(attempt, { duration: "3 seconds", orElse: () => send("SIGKILL") })
    • Then Deferred.await(signal) — wait for the process to actually exit
  4. If the process group has lingering child processes (e.g. bun spawning tsgo, node, or file watchers), the exit signal may not arrive even after SIGKILL.
  5. When handle.kill() does not resolve, Effect.orDie does not help because the Effect itself is still running.
  6. The shell tool never returns a result, so the tool part stays status: "running" in the database.
  7. The child session never reaches idle or error, so the parent task() call blocks indefinitely.

Observed case

  • Command: bun run i18n:sync && bun run typecheck && bunx oxlint src/features/gateway-playground/index.tsx src/components/layout/components/app-sidebar.tsx src/hooks/use-sidebar-data.ts
  • Working directory: project web/default directory
  • Timeout parameter: 120000 (2 minutes)
  • Actual behavior: Tool part remained status: "running" for 19+ minutes. No completed or error state was written. Child session stayed busy. Parent session blocked on task().
  • Same command run manually in terminal: Completed successfully in ~2 minutes with exit code 0.
  • No lingering processes: After the issue, ps showed no bun, tsgo, or oxlint processes still running, yet the database tool part was still running.

This confirms the process was already dead but the tool state was never finalized.

Expected behavior

  • After timeout, the shell tool should always finalize the tool part as completed (with timeout metadata) or error, regardless of whether handle.kill() succeeds.
  • If handle.kill() does not resolve within the forceKillAfter grace period, the tool should still write a terminal state to the database.
  • The child session should receive session.error or session.idle.
  • The parent task() call should receive a structured failure instead of blocking forever.

Suggested fix

Add a hard finalization guarantee after handle.kill():

if (exit.kind === "timeout") {
  expired = true
  yield* handle.kill({ forceKillAfter: "3 seconds" }).pipe(
    Effect.timeoutOrElse({
      duration: "5 seconds",
      orElse: () => Effect.sync(() => {
        // Force-write tool state even if kill cleanup is stuck
        console.error("[shell] kill cleanup timed out, force-finalizing tool part")
      }),
    }),
    Effect.orDie,
  )
}

Or wrap the entire post-timeout block in a hard timeout that guarantees the tool returns a result.

Plugins

oh-my-openagent

OpenCode version

1.18.4

Steps to reproduce

  1. Launch a subagent through the Task tool.
  2. Have it run a compound shell command like bun run i18n:sync && bun run typecheck && bunx oxlint ... with timeout: 120000.
  3. If the command's process tree has any child processes that don't exit cleanly with the parent, the timeout fires but handle.kill() hangs.
  4. Observe: tool part stays running indefinitely, child session stays busy, parent task() blocks forever.

Operating System

macOS

Terminal

OpenChamber / OpenCode server

Activity

  1. github-actions commented on Jul 22, 2026

    @github-actions
    Contributor

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

    If this is intentional (e.g. you meant to provide additional technical detail), please consider closing one and linking the other, or adding a comment to #38276 with the root cause analysis instead.

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