anomalyco / anomalyco/opencode

SSE /global/event and /event deliver only server.connected + heartbeats — no message events reach any subscriber (1.18.25)

Open
#46,733 0 comments 0 reactions 1 assignee View on GitHub

@neriousy is already working on this.

Since Sep 2, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description

On OpenCode 1.18.25 (managed by OpenChamber 1.22.0), the SSE event streams (/event and /global/event) accept connections and deliver server.connected + server.heartbeat, but no message events (message.part.updated, message.updated, session.status, session.idle, etc.) are ever delivered to any SSE subscriber — including direct localhost connections with no proxy in the path.

Sessions run normally: tokens stream, the LLM responds, session.messages() returns the completed content. But live event delivery to SSE clients is completely broken. A manual page refresh shows the updates (the server has the data), but real-time updates never arrive.

This reproduces on every client — desktop browser on localhost:5555 (no proxy), desktop browser over Tailscale Serve HTTPS, and mobile browser over Tailscale. It is not proxy-specific or mobile-specific.

Environment

  • OS: Arch Linux (kernel 6.x, x86_64)
  • OpenCode: 1.18.25 (binary at ~/.opencode/bin/opencode, dated Aug 28)
  • OpenChamber: 1.22.0 (manages the OpenCode instance, port 5555)
  • Runtime: Bun (bundled in the OpenCode binary)
  • Provider: ollama-cloud (glm-5.2) — also reproduced with other providers
  • No reverse proxy on the localhost path (OpenChamber binds 0.0.0.0:5555 directly)
  • Tailscale Serve is configured but the bug reproduces without it

Steps to reproduce

  1. Start OpenCode via OpenChamber (or opencode serve directly).
  2. Connect to the SSE stream:
    curl -sN -u "opencode:<password>" \
      -H "Accept: text/event-stream" \
      http://127.0.0.1:<port>/global/event
    
  3. In a separate terminal, send a prompt to a session (or use an active session in OpenChamber).
  4. Observe the SSE output: only server.connected and server.heartbeat arrive. No message.*, session.status, session.idle, or any other content events.

Expected behavior

The SSE stream delivers message.part.delta, message.part.updated, message.updated, session.status, session.idle, etc. as the session processes — as it did in earlier versions.

Actual behavior

Only server.connected and server.heartbeat are delivered. No message events. The session runs to completion server-side, but no subscriber sees it live.

Evidence

1. SSE stream output (OpenCode /global/event directly, with auth, while session active)
data: {"payload":{"id":"evt_...","type":"server.connected","properties":{}}}

data: {"payload":{"id":"evt_...","type":"server.heartbeat","properties":{}}}

data: {"payload":{"id":"evt_...","type":"server.heartbeat","properties":{}}}

25-second capture during an actively streaming session. Only heartbeats.

2. OpenCode log shows ZERO bus publish lines

In 357,019 lines of ~/.local/share/opencode/log/opencode.log, there are zero service=bus type=... publishing lines. The session is actively processing (process, stream, loop, llm runtime selected all appear), but events are not being published to the bus — or at least not logged as such, and not delivered to SSE subscribers.

For comparison, the older reports (#26866, #27966) showed service=bus type=message.part.updated publishing in the server log even when SSE delivery was broken. Here, those lines are absent entirely.

3. OpenChamber's SSE fan-in confirms the same

OpenChamber proxies from OpenCode's /global/event to its own /api/global/event. Testing OpenChamber's endpoint directly shows the same symptom — only server.connected + heartbeats — confirming the break is upstream in OpenCode, not in OpenChamber.

4. Manual refresh works

Reloading the page in any browser shows the completed session content. The server has the data; only live event delivery is broken.

5. Not proxy-specific

Reproduces on:

  • desktop browser, no proxy
  • desktop browser, raw Tailscale IP, no proxy
  • desktop + mobile, Tailscale Serve HTTPS proxy

All three show the same symptom.

Related issues

This matches a known class of SSE event-delivery regression:

  • #39729 — "Web UI: a tab's /global/event stream silently stops delivering when several tabs are connected" — identifies the GlobalBus listener leak as the root cause. The acquireRelease finalizer never runs on disconnect, so listeners accumulate and silently kill event delivery. Reproduced on 1.18.5.
  • #28492 — the underlying GlobalBus listener leak. Shows 16 connections → 16× "global event connected", 0× "global event disconnected".
  • #31922 — the fix for #39729/#28492 (+130/-17 lines, touches global.ts, event.ts). Written but never reviewed — closed by the automated PR-cleanup bot for staleness.
  • #44782 — "opencode run hangs after 'event connected'" on 1.18.21. Same version line, same "event connected then nothing" symptom. Assigned to @jlongster, still open.
  • #41986 — "[global-sdk] event stream failed race condition" on 1.18.16+. UI hangs indefinitely after server ready.
  • #27966 — the original SyncEvent delivery regression (1.14.42+), supposedly fixed in 1.15.5 via #27825, #28051, #27959, #28187. The symptom matches exactly.

Notes

  • 1.18.26 (latest) does not fix this — its release notes only mention Claude 5 thinking blocks, Bedrock GPT-5.6, and tool call timing. Nothing about SSE/GlobalBus.
  • The fix (PR #31922) exists but was never merged. Re-landing it may resolve this.
  • This breaks all real-time usage of OpenCode through any SSE client (OpenChamber, custom integrations, etc.).
Plugins

No response

OpenCode version

1.18.25

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.