[BUG] ccd-cli --resume does not replay historical messages after bridge restart — sessions appear empty

Resolved 💬 2 comments Opened May 13, 2026 by parseN520 Closed May 13, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

After restarting the remote bridge (~/.claude/remote/server --bridge), Claude Desktop's sidebar continues to show existing session titles, but selecting any session displays "No messages yet". The .jsonl transcript files on disk are intact and valid, but the freshly spawned ccd-cli --resume <session-id> process never replays the historical messages to stdout, so the client renders an empty conversation.

ENVIRONMENT

  • OS: Linux 5.15 (Ubuntu) — server side
  • Client: Claude Desktop (entrypoint reported as claude-desktop in session metadata), connected to the server via SSH (sshd is the parent of the bridge process)
  • Binary: ~/.claude/remote/ccd-cli/2.1.138
  • Also reproduced with: 2.1.121 and 2.1.128 (same behavior — not a regression)
  • Bridge / server: ~/.claude/remote/server (--serve and --bridge modes)

What Should Happen?

When clicking an existing session, the freshly spawned ccd-cli --resume <id> --replay-user-messages should stream the historical type:"user" / type:"assistant" records from the on-disk .jsonl transcript to stdout, and the client should render the full conversation.

Either:

  1. The binary pushes the buffered transcript automatically on stdout after control_request:initialize, OR
  2. The client issues an explicit control_request:replay_history and the binary responds with the same stream.

Currently only the process.reattach path (to a long-lived process) renders the history; the cold process.spawn path leaves the UI empty despite the transcript being intact on disk.

Error Messages/Logs

## Evidence

### 1. Transcripts on disk are valid

Parsed the largest transcript:


Size: ~32 MB
Lines: 9,963 valid JSONL records (0 parse errors)
Types: assistant=4931, user=3139, last-prompt=734, attachment=406,
       queue-operation=364, ai-title=260, system=129


All transcripts in the project's `.claude/projects/<cwd-slug>/` directory parse cleanly (totaling ~27,000 records across multiple sessions).

### 2. The `ccd-cli` binary does not replay
Direct test, bypassing the bridge entirely:
```bash
{ echo '{"request_id":"t","type":"control_request","request":{"subtype":"initialize","hooks":{}}}'; sleep 8; } \
  | timeout 10 ~/.claude/remote/ccd-cli/2.1.138 \
      --output-format stream-json --input-format stream-json --verbose \
      --resume <SESSION_UUID> \
      --replay-user-messages --include-partial-messages \
  > /tmp/stdout.log 2> /tmp/stderr.log

Result:

STDOUT: ~7 KB, 1 line
        → 1 × control_response/success
        → 0 × user / assistant / replay events
STDERR: 0 bytes (no errors reported)

Same result on 2.1.121 and 2.1.128.

The binary contains the symbols (replayBufferedMessages, replayUserMessages, --replay-user-messages) — the feature is present in the source but produces no output during --resume.

3. Bridge log shows the client never receives history

Methods observed in remote-server.log after a fresh session selection:

process.spawn → spawns new ccd-cli with --resume
process.stdin (initialize) → sends control_request
process.stdin (mcp_set_servers)
process.stdin (get_context_usage)

No further inbound traffic from the new ccd-cli that contains transcript messages.

Process file descriptor inspection (/proc/<pid>/fd/) confirms the running ccd-cli has no .jsonl open during steady-state — it either never opens it, or opens it once at startup and closes it without emitting the parsed records.

Workarounds

  • None for the UI. After this state, sessions cannot be re-rendered without the messages being replayed.
  • The on-disk .jsonl files remain authoritative and can be parsed manually for content recovery.
  • A full kill of --serve + Desktop restart was not attempted during debugging (would have destroyed the active session used to investigate), but is likely the only way to recover.

### Steps to Reproduce

1. Have Claude Desktop connected to a remote backend with several active sessions (each backed by a `ccd-cli --resume <id>` process held in memory by the `--serve` daemon).
2. Restart the bridge:
   ```bash
   kill $(pgrep -f "remote/server --bridge")
   ```
   The client auto-reconnects and the bridge respawns.
3. The previously held `ccd-cli --resume` processes become orphaned (their RPC `process.reattach` UUIDs are no longer mapped in the new bridge). Killing them or letting the client spawn new ones is equivalent for this bug.
4. Click an existing session in the sidebar.
5. The client issues `process.spawn` → `control_request:initialize` → `mcp_set_servers` → `get_context_usage` to the new `ccd-cli`.
6. **Expected**: historical messages from the session's `.jsonl` are streamed back and rendered.
7. **Actual**: only the `control_response:success` is returned; no `type:"user"` / `type:"assistant"` events from the transcript ever appear. UI shows "No messages yet."

### Claude Model

claude-opus-4-7

### Is this a regression?

I don't know

### Last Working Version

_No response_

### Claude Code Version

2.1.138 (Claude Code)

### Platform

Anthropic API

### Operating System

Ubuntu/Debian Linux

### Terminal/Shell

BASH

### Additional Information

## Hypothesis

`--replay-user-messages` appears to assume the historical replay happens within a process the user is interactively typing into, not on a fresh `--resume` cold-start triggered by `process.spawn`. The client expects the binary to push the transcript through stdout after `initialize`, but the binary does not — leaving the client and the binary in a deadlock where neither side knows to drive the replay.

The pre-bridge-restart state worked because the original `ccd-cli` processes had been alive since their first interactive launch, with messages already buffered in-process; the client reattached via `process.reattach` and rendered from the in-memory buffer rather than via a fresh replay. Restarting the bridge dropped the `process.reattach` UUID map, forcing a cold `process.spawn` path that exposes the missing replay.

## Severity

- **Data loss risk**: none — transcripts are on disk
- **Recovery cost**: high — every restart of `--bridge` (intentional or accidental) makes all sessions appear empty in the UI until the binary is fixed
- **User impact**: medium — sessions are present but unusable through the UI

## Suggested fix

Either:
1. Make `ccd-cli --resume X --replay-user-messages` emit the buffered `user` / `assistant` records on stdout immediately after the first `initialize` response, OR
2. Add an explicit RPC method (e.g. `control_request:replay_history`) the client can issue to trigger replay on demand, and have the client call it after spawning a `--resume`d process.

---

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗