Skip to content

OpenCode leaves an orphaned process when it itself is killed #11527

Closed

Description

@TomasHubelbauer

Description

When I run OpenCode programatically using await Bun.spawn(["opencode"]), it spawns a sub-process moments after starting up which then becomes orphaned (it deparents from the opencode process that spawned it and its PPID becomes 1).

When I kill the OpenCode instance I started by PID, it won't kill the sub-process since it has deparented.

Bun will keep running until the orphaned process dies as it was never unref'd from Bun's PoV (only from the PoV of the Open Code process that spawned it).

This is a problem for use-cases where OpenCode is invoked programmatically (Git hooks for qualitative validations, solutions for terminal mirroring not based on SSH but on Bun's Terminal API etc.)

Plugins

none

OpenCode version

1.1.48

Steps to reproduce

Run:

console.log("Killing all OpenCode instance to ensure clean slate…");
await Bun.$`killall -KILL opencode`.nothrow();

console.log("Printing running OpenCode instances…");
console.log("\t(There should be none)");
await Bun.$`pgrep -fl opencode`.nothrow();

console.log("Starting OpenCode…");
const proc = await Bun.spawn(["opencode"]);
console.log("Started OpenCode. PID:", proc.pid);

const timeout = 100;
console.log(`Waiting for ${timeout} ms for the orphan (worker?) to deparent…`);
await Bun.sleep(timeout);

console.log(`Killing OpenCode with PID ${proc.pid}…`);
proc.kill("SIGKILL");
await proc.exited;
console.log(`Killed OpenCode with PID ${proc.pid}. ${proc.signalCode}`);

console.log("Printing running OpenCode instances…");
console.log("\t(There should be none, but there is one with PPID 1 - orphan)");
await Bun.$`pgrep -fl opencode`.nothrow();

console.log("Killing self to prevent blockage due to ref'd deparented process");
process.exit();

You will see that it starts off with no orphans, then it spawns OpenCode, waits for OpenCode to spawn the sub-process (maybe some worker?), but then the sub-process immediately becomes deparented. Next, the OpenCode instance that was started gets killed and we query running opencode processes - there should be one, but the one orphan remains.

This script is re-entrant, on the next run, it will kill the orphan, run again and leave another orphan, demonstrating the problem.

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. github-actions commented on Jan 31, 2026

    @github-actions
    Contributor

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

    These issues collectively point to a systemic problem with process lifecycle management in OpenCode - subprocesses aren't being properly tracked or cleaned up when parent processes are killed.

    Feel free to ignore if your specific case is different from these issues.

  2. TomasHubelbauer commented on Jan 31, 2026

    @TomasHubelbauer
    Author

    FYI I am using no MCP servers, the orphaned process is /Users/tom/.bun/install/global/node_modules/opencode-darwin-arm64/bin/opencode.

  3. added a commit that references this issue on Feb 23, 2026
    7a5a9bd
  4. github-actions commented on Apr 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 90 days of no activity. If the issue is still relevant please open a new one.

  5. TomasHubelbauer commented on Apr 3, 2026

    @TomasHubelbauer
    Author

    LOL

  6. ethanhuang996-alt commented on Jun 27, 2026

    @ethanhuang996-alt

    复现确认:OpenCode 在 Windows 上的孤儿子进程问题

    我们在 Hermes Agent 环境下成功复现了 OpenCode 子进程孤儿问题。

    根因分析

    经过对 OpenCode 生成的 Python 脚本的二进制检查,确认 OpenCode 在 Windows 上 spawn 子进程时使用了 detached: true 标志(已在二进制字符串中直接确认)。由于 OpenCode 是 Bun 编译的二进制文件,无法直接修改其源码。

    解决方案(Windows 专用):autokill.py

    我们编写了一个 33 行的 watchdog 模块(autokill.py),内置于 OpenCode 生成的 Python 脚本中。原理是用 ctypes 调用 Windows kernel32 API 的 WaitForSingleObject,每 2 秒检测父进程是否存活。当父进程死亡时,脚本自动执行 os._exit(1) 退出,避免成为孤儿进程。

    实战验证

    在 Hermes Agent 环境下实测:杀死 bash/opencode 进程后,Python 脚本在 2 秒内检测到父进程死亡并自动退出,不再有孤儿进程残留。

    完整代码

    # autokill.py - 父进程死亡时自动自杀
    # 用法:任何从 opencode 启动的 Python 脚本,开头 import 即可
    # import autokill; autokill.init()
    
    import os, sys, threading, time
    
    def _watchdog():
        try:
            import ctypes.windll.kernel32 as k32
        except Exception:
            return
        ppid = os.getppid()
        handle = k32.OpenProcess(0x0400 | 0x00100000, False, ppid)
        if not handle:
            return
        while True:
            time.sleep(2)
            if k32.WaitForSingleObject(handle, 0) == 0:
                os._exit(1)
            current_ppid = os.getppid()
            if current_ppid != ppid:
                os._exit(1)
    
    def init():
        t = threading.Thread(target=_watchdog, daemon=True)
        t.start()

    使用方式

    在任何从 OpenCode 启动的 Python 脚本开头加入:

    import autokill
    autokill.init()

    该方案仅适用于 Windows 平台(依赖 ctypes.windll.kernel32),在其他平台上 import 会自动静默跳过。

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions