Skip to content

fix(client): use PilotError type check instead of fragile substring match in subscribe_event (PILOT-187)#2

Open
matthew-pilot wants to merge 1 commit into
mainfrom
openclaw/pilot-187-20260528-033419
Open

fix(client): use PilotError type check instead of fragile substring match in subscribe_event (PILOT-187)#2
matthew-pilot wants to merge 1 commit into
mainfrom
openclaw/pilot-187-20260528-033419

Conversation

@matthew-pilot
Copy link
Copy Markdown
Collaborator

What failed

subscribe_event in client.py:1061 used fragile string matching to detect clean disconnects:

if "connection closed" in str(e).lower() or "EOF" in str(e):

The "EOF" in str(e) branch is too broad — any error containing "EOF" (e.g., RuntimeError("unexpected EOF on stream")) was silently swallowed as if it were a clean disconnect. This means a real connection error during event streaming would be silently lost rather than propagated.

Why this fix

Replace the substring check with isinstance(e, PilotError) so only the library's own PilotError instances are treated as recoverable disconnects. The PilotError("connection closed") path (raised by the C-bound Conn class when the underlying Go connection closes) is the legitimate clean-disconnect signal.

Changes

  • pilotprotocol/client.py (+1/-1): isinstance(e, PilotError) instead of fragile substring match
  • tests/test_services.py (+7/-3): test_eof_error_breaks_cleanlytest_eof_error_propagatesRuntimeError containing "EOF" is not a clean disconnect and should propagate

Verification

  • uv sync --extra dev
  • uv run pytest204 passed, 90 skipped, 100% coverage

Closes PILOT-187

…atch in subscribe_event (PILOT-187)

The subscribe_event loop used str(e) substring matching to detect
clean disconnects: "connection closed" or "EOF".  The "EOF" branch is
too broad — any error message containing "EOF" (e.g. RuntimeError
"unexpected EOF on stream", or a hypothetical "EOFError raised
mid-frame") was silently swallowed even though the underlying error
was not a clean disconnect.

Replace the substring check with isinstance(e, PilotError) so only
the library's own PilotError instances are treated as recoverable
disconnects.  The PilotError("connection closed") path (raised by the
C-bound Conn class when the underlying Go connection is closed) is the
legitimate clean-disconnect signal.

The related timeout semantics (loop timeout vs per-read deadline) are
documented in the existing docstring; no behavioral change there.

Closes PILOT-187
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

1 participant