Remote Control: responses never render in browser until manual page refresh (iPad Safari/Chrome, macOS Safari)
Summary
Assistant responses never render in the browser. The page shows nothing for the response until I manually reload, after which the full response appears. This happens for every response, not just long ones.
Reproduces consistently across two devices and three browser/OS combinations, so it does not look device-specific.
Steps to reproduce
- Open a Claude Code session in the browser (Remote Control).
- Send any message.
- Wait.
Expected
The response streams in and appears incrementally.
Actual
Nothing from the response is displayed. The area stays empty no matter how long I wait. Manually refreshing the page (Cmd-R / pull-to-refresh) makes the complete response appear immediately.
Because a refresh always surfaces the response, the server side appears to be generating and persisting it correctly — only the live delivery/render path to the browser fails. This looks like the streaming (SSE) connection either never delivers events or the client never renders them, with no visible error or timeout.
Environment
- iPad — Safari: reproduces
- iPad — Chrome: reproduces
- iMac (macOS) — Safari: reproduces
Exact OS and browser versions not captured; happy to add them on request.
Impact
Every response requires a manual page refresh to read, which makes the browser client effectively unusable for normal back-and-forth.
Additional notes
Both a mobile Safari/Chrome (which on iOS share WebKit) and a desktop Safari are affected. I have not yet tested desktop Chrome or Firefox — can do so if that would help narrow it down.
5 Comments
Additional environment detail I should have included: the local
claudeprocess is running inside tmux (tmux session,TERM=screen), not in a bare terminal.Flagging it because the symptom may fit a tmux interaction: if the browser view mirrors terminal state, incremental redraws may not propagate through tmux, while a page refresh forces a full re-capture of the pane — which would explain why the complete response appears only after a manual reload.
I have not yet tested running
claudeoutside tmux. Will follow up with that result. If it turns out to be tmux-specific, the title/framing of this issue should be narrowed accordingly.Follow-up: tmux is ruled out.
Re-tested with
claudestarted directly in Terminal on the iMac — no tmux, no ssh, fully local — then enabled Remote Control and connected from the browser. Behaviour is identical: nothing from the response renders until a manual page refresh, at which point the full response appears.So the earlier tmux note is not the cause; please disregard it. The original report stands as filed: this reproduces in a plain local session, on every response, across iPad Safari, iPad Chrome, and macOS Safari.
Device/OS details:
Safari versions are whatever ships with those OS releases (on iPadOS it is not separately updatable). Note the two Safari builds are several major versions apart and both are affected, as is Chrome on iPadOS.
Also reproducible on Firefox ESR 140.13.0 (Debian Trixie), not just Safari/Chrome as the title suggests — so this isn't platform-specific.
Debugging done on my end:
101 Switching Protocols)So the WebSocket connection and data delivery work fine; the bug is client-side, in how (or whether) the UI re-render is triggered after an incoming WS frame — and it seems browser-dependent rather than isolated to Safari/Chrome.
mouzaf’s trace is the useful fork here. If the complete assistant frame reaches the browser and a reload reconstructs the same message, I wouldn’t spend the next pass on reconnect/SSE. I’d carry one sequence number through four probes: WS callback → decoded event → session reducer → store subscriber. The first missing sequence tells you whether this is routing/decode, an in-place mutation that preserves snapshot identity, or a subscriber that never fires.
The regression test I’d want mounts an existing session, feeds a real assistant frame through the production WS callback, and asserts the reply appears without navigation; then repeats after switching away and back. A reducer-only test can stay green while the React subscription bridge is broken.
I maintain BitFun, and we give each connection’s event streams explicit cursors at the app-server boundary: https://github.com/GCWing/BitFun/blob/main/src/crates/interfaces/app-server/src/server.rs#L20-L80. That split has been handy precisely because a healthy socket is not proof of a current view.