Skip to content

TUI sometimes never draws: first RPC calls to the server worker are lost #55949

Closed as not planned

Description

@shunkakinoki

Description

Sometimes the TUI starts and never draws anything. The pane stays blank forever, both threads sit idle in epoll_wait, the log never reaches creating instance, and the worker never opens the database.

Cause: tui.ts creates the server worker and immediately calls client.call("fetch", ...) (and checkUpgrade a second later). The worker only runs Rpc.listen(rpc) on its last line, after module evaluation, which includes the top-level await in packages/core/src/global.ts. Bun drops messages that reach a worker before its onmessage handler is set when the worker module has a top-level await. Rpc.client().call() has no timeout or rejection, so the TUI waits on a reply that never comes.

This is a race, so it only shows up when the worker's module evaluation is slow enough (busy host, many TUIs starting together). On our fleet of unattended agents it was the most common way a session failed to start: 13 blank sessions in 6.5 hours across three Linux hosts.

Plugins

None needed to reproduce

OpenCode version

1.18.31, also present on v1 at 1.19.2

Steps to reproduce

Minimal Bun repro without OpenCode:

// worker.ts
await new Promise((r) => setTimeout(r, 1000)) // stands in for the top-level await in global.ts
onmessage = (e) => postMessage("pong " + e.data)
// main.ts
const w = new Worker(new URL("./worker.ts", import.meta.url).href)
w.onmessage = (e) => console.log(e.data)
setTimeout(() => w.postMessage("ping"), 500)
setTimeout(() => process.exit(0), 3000)

bun main.ts prints nothing: the ping is lost (5/5 runs, Bun 1.4.3). With a ready handshake (worker posts ready after setting onmessage, main waits for it before posting) it prints pong ping 5/5. Across three timing variants on Linux: 15/15 lost without the handshake, 15/15 delivered with it.

In OpenCode itself: start many opencode TUIs in a loop on a loaded machine; a fraction never render.

Operating System

Linux (NixOS)

Terminal

tmux-style multiplexer panes

Activity

  1. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed analysis and the minimal Bun repro, it makes the race very clear.

    This report is about OpenCode v1 (1.18.x/1.19.x). v1 now only gets fixes for critical bugs (data loss, security problems, or breakage with no workaround). An intermittent hang at startup that a restart gets past most likely doesn't meet that bar, so this is unlikely to be fixed in v1. Your PR #55950 is linked here, and maintainers can still look at it.

    v2 (@opencode/cli, versions 2.x) is a separate implementation. Its TUI doesn't talk to a server worker through Rpc.listen the way v1 does, so this race shouldn't happen there. We'd encourage upgrading to v2, especially for unattended agent fleets.

    Related items:

    If you think this is critical, or you see blank sessions on v2 too, please reply here with the details.

  2. shunkakinoki commented on Oct 16, 2026

    @shunkakinoki
    Author

    For unattended agents there is no workaround short of killing and relaunching the process, and the hang rate on a busy Linux host is high: 8/30 and 5/30 TUI launches never drew in a lane-like loop on 1.18.31. The fix in #55950 is 7 lines in util/rpc.ts with a regression test, and leaves the protocol otherwise unchanged. We carry it as a local patch until then, so a v1 point release would let us drop it.

  3. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Thanks for the numbers. A 17–27% failure rate on a busy host is a lot for an unattended fleet, and I understand that kill-and-relaunch is a clumsy workaround in automation.

    Under v1's policy, though, this still counts as an intermittent startup hang that a relaunch gets past, with no data loss or security problem, so it doesn't meet the bar for v1 fixes. Your PR #55950 is small, has a regression test and is linked to this issue. Whether it goes into a v1 point release is for maintainers to decide, and I can't promise that.

    Until then, two options:

    • Keep your local patch, or add a startup watchdog to your harness that relaunches a session if the TUI hasn't started within a short timeout.
    • Move the fleet to v2 (@opencode/cli). Its TUI doesn't talk to a server worker through Rpc.listen, so it shouldn't have this race. If you see blank sessions on v2, please open a new issue with the details and we'll look into it.
  4. opencode-agent commented on Oct 17, 2026

    @opencode-agent
    Contributor

    Closing this as not planned: it's a v1 issue, and v1 only gets fixes for critical bugs. As discussed above, this startup race can be worked around by relaunching, so it doesn't meet that bar. Thanks again for the very clear analysis and repro.

    Your fix in #55950 is still linked here for maintainers to review. v2 (@opencode/cli) doesn't use this TUI worker setup, so moving to v2 is the best way to avoid the race. If you see blank sessions on v2, please open a new issue with the details.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions