Skip to content

[Web] WS never pushes events + transcript API serves stale in-memory snapshot (0.42.0, macOS) — messages invisible until server restart #3722

Description

@fpeng456

Summary

On macOS, the kimi web server accepts WebSocket connections and subscriptions but never pushes any session events, while its GET /sessions/{id}/transcript REST API serves a stale in-memory snapshot that never observes turns written by other processes (e.g. the terminal CLI). User-visible symptoms: web UI permanently stuck at "connecting…", sent messages appear to be swallowed, and the only workaround is restarting the server.

All evidence below was collected with protocol-level tooling (CDP-injected WebSocket sniffer, raw WebSocket reproduction, direct wire.jsonl comparison).

Environment

  • kimi-code CLI/Server 0.42.0 (same class of symptoms since 0.41.0)
  • macOS 26.6.2, MacBook Pro
  • Server managed by launchd, listening on 127.0.0.1:58627
  • Control group: same version on a fresh Ubuntu (Hyper-V/WSL) machine works fine → likely tied to local server-side state

Symptoms

  1. Open any session (including brand-new empty ones) in the bundled web UI
  2. Footer permanently shows "connecting… opening session…"; sending a message produces no echo, toast "WebSocket error"
  3. After F5, history up to the latest fold point renders (REST + transcript fold work), but realtime events never arrive

Protocol-level evidence

Page's own WS client frames (CDP sniffer):

NEW: ws://127.0.0.1:58627/api/v1/ws?client_id=web_…
OPEN
MSG:  server_hello (protocol_version 2, heartbeat_ms 10000)
SEND: client_hello → ack code:0 success
SEND: subscribe + subscribe_v2 → both ack code:0 success, session accepted
MSG:  transcript.reset (seq 803, epoch ep_…)   ← the ONLY session-related push ever seen
Then: ping/pong heartbeats only — zero transcript deltas / session events,
      even while the agent is actively executing tool calls in that session
  • Reproduced identically with a raw new WebSocket(...) (subprotocol kimi-code.bearer.<token>): handshake + subscription acks succeed, zero events afterwards
  • Server log has no error-level entries; REST requests all 200

Root cause confirmed: stale in-memory transcript snapshot

While building a minimal read-only web client against the official backend, we confirmed:

  • Disk wire.jsonl had reached turn 232, while GET /sessions/{id}/transcript returned at most turn 220 — turns written by the terminal CLI process are invisible to the web server process.
  • Session list metadata (last_prompt, updated_at) is fresh — the list knows there are updates, the transcript endpoint can't serve them.
  • Restarting the web server forces a re-read from disk and temporarily fixes it — matching the long-standing user symptom "must restart kimi web to see new messages".

Two additional wire-protocol defects found

  1. turnId is reused after full compaction: the first new turn after a full compaction reused an old turnId from days ago (both old and new turn had id 217; later turn 234 did not). Any client merging by turnId will append new content into a stale mid-list turn object — new messages "disappear", the old turn's prompt gets overwritten.
  2. Frames may arrive after turn.ended: trailing content.part frames with the same turnId were observed after turn.ended. Clients that destroy the turn object on ended will spawn ghost turns.

Transcript fold pipeline freezes periodically

On the same server, fold advances for only a few minutes after each restart:

server start frozen at fold stuck at
09:42 ~09:44 t102
10:31 ~10:50 t108
10:47 (after full state reset) ~10:50 t108 (30 min no progress; wire.jsonl gained 16 turns in the same window)

Also observed: session index reconciliation failed: AggregateError: batch failed on 1/9 shard(s) repeating ~325 times over multiple days (later became ENOENT on a missing shard dir after we moved the query-store away). A 40MB corrupt query-store backup is preserved and can be provided privately if useful.

Exclusions

  • Browser cache/extensions/profile — reproduced in a clean browser profile
  • Frontend/server version mismatch — the bundled frontend version is as shipped
  • Session data corruption — wire.jsonl keeps appending normally; fold catches up after restart
  • Full server state reset (query-store, search-index, session_index.jsonl all rebuilt) — fold recovers, WS push still silent

Workaround that proves the fix direction (reference implementation)

We built a ~500-line temporary read-only web client that has run 24h+ without errors:

  1. Transcript read directly from wire.jsonl (append-only, visible to all processes), incremental parsing with cached file offset — messages never "disappear", no restarts needed
  2. Send/abort/file upload still go through the official REST API — proving only the read path is broken, the write path is healthy
  3. Client-side compatibility rules for the wire defects: pair tool.result by toolCallId (it has no turnId); clear turnId mappings on context.apply_compaction; keep turn objects after turn.ended; determine "running" from the last turn-kind entry + wire mtime

Note: this report was investigated and assembled by Kimi Code itself (with user authorization) — which shows how high the debugging bar is for regular users. A self-service "reset server event pipeline state" switch would already help a lot.

Expected

  • Fix the server-side event push pipeline (or document where its state lives so it can be cleared/rebuilt manually)
  • transcript endpoint should treat wire.jsonl as the source of truth (invalidate/re-read the in-memory snapshot)
  • Never reuse turnId after compaction (or provide a globally monotonic turn sequence)
  • Document the turn lifecycle: trailing frames after ended are legal

Activity

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

Metadata

Metadata

Assignees

No one assigned

    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