Skip to content

fix(client): survive handshake from a protocol-incompatible daemon - #238

Merged
georgeh0 merged 1 commit into
mainfrom
fix/stale-daemon-handshake-decode
Jul 24, 2026
Merged

fix(client): survive handshake from a protocol-incompatible daemon#238
georgeh0 merged 1 commit into
mainfrom
fix/stale-daemon-handshake-decode

Conversation

@georgeh0

Copy link
Copy Markdown
Member

Fixes #237.

Summary

Upgrading 0.2.37 → 0.2.38 bricked every ccc command with ValidationError: Object missing required field 'pid'. Root cause: 0.2.38 (#214) added a required pid field to HandshakeResponse, so the reply of a still-running pre-upgrade daemon no longer decoded — the decode error fired before the client could see the ok=False version mismatch and restart the daemon. The same uncaught error also broke ccc daemon stop, i.e. the recovery path itself.

Three layers of fix:

  • Wire compat: HandshakeResponse.pid gets a default (None), with a comment stating the rule — handshake fields added after a release must have defaults, since the handshake is the one message exchanged between mismatched client/daemon versions. With this alone, a stale 0.2.37 daemon's reply decodes, ok=False is seen, and the normal restart path fires.
  • Decode hardening: an undecodable handshake reply now raises DaemonProtocolError instead of escaping as a raw msgspec error; _connect_and_handshake treats it like a version mismatch (restart on first contact, fail fast once a matching daemon was already ensured). This protects against any future wire drift, not just this field.
  • Recovery path: stop_daemon tolerates the handshake failure and falls through to its SIGTERM/SIGKILL escalation, so ccc daemon stop always works against an incompatible daemon.

Workaround for users hitting this on 0.2.38 today: pkill -f cocoindex_code.daemon (or kill the PID from ~/.cocoindex_code/daemon.pid), then rerun any ccc command.

Test plan

  • New test: a pre-0.2.38 handshake reply (no pid field) decodes (test_decode_handshake_response_from_pre_0_2_38_daemon)
  • New test: undecodable handshake → daemon stopped and restarted, request served (test_connect_restarts_daemon_on_undecodable_handshake)
  • New test: undecodable handshake after an ensured daemon fails fast without restart loop (test_connect_fails_fast_on_undecodable_handshake_after_ensured)
  • New test: stop_daemon reaches the kill escalation instead of crashing (test_stop_daemon_escalates_past_undecodable_handshake)
  • Full pre-commit suite green locally (ruff, ruff-format, mypy, full pytest)

🤖 Generated with Claude Code

Fixes #237. Upgrading 0.2.37 → 0.2.38 bricked every ccc command with
"ValidationError: Object missing required field `pid`": 0.2.38 added a
required `pid` field to HandshakeResponse, so the reply of a still-running
pre-upgrade daemon no longer decoded — before the client could see the
`ok=False` version mismatch and restart it. The same uncaught error also
broke `ccc daemon stop`, the recovery path.

Three layers:

- `HandshakeResponse.pid` gets a default (None), with a comment stating
  the wire-compat rule: handshake fields added after a release must have
  defaults, since the handshake is the one message exchanged between
  mismatched versions.
- An undecodable handshake reply now raises `DaemonProtocolError` instead
  of escaping as a raw decode error; `_connect_and_handshake` treats it
  like a version mismatch (restart on first contact, fail fast once a
  matching daemon was ensured). This protects against any future wire
  drift, not just this field.
- `stop_daemon` tolerates the decode failure and falls through to its
  SIGTERM/SIGKILL escalation, so `ccc daemon stop` always works.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@georgeh0
georgeh0 merged commit fd99851 into main Jul 24, 2026
4 checks passed
@georgeh0
georgeh0 deleted the fix/stale-daemon-handshake-decode branch July 24, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error ValidationError: Object missing required field pid after upgrade to 0.2.38

1 participant