Skip to content

Say goodbye before dropping the socket, and specify that - #2

Merged
tactino merged 1 commit into
mainfrom
fix/client-close-frame
Sep 11, 2026
Merged

tactino merged 1 commit into
mainfrom
fix/client-close-frame

Conversation

@tactino

@tactino tactino commented Sep 11, 2026

Copy link
Copy Markdown
Member

All 45 server logs from E7 ended with ConnectionClosedError: no close frame received or sent. The C++ client dropped the socket without a
WebSocket close frame. It went unnoticed because in every previous
conformance run the server ran out of steps first; E7 is the first
experiment where the client finishes first, which is what a real env client
does when it reaches its episode budget.

Three things were wrong:

  • the client did not send a close frame - it now sends status 1000 per
    RFC 6455 section 5.5.1;
  • the spec had a hole - section 7 covered four ways the server closes
    and nothing about the client stopping. New section 7.5;
  • the conformance server was stricter than the spec, counting any
    disconnect as a violation. That is the failure its two severity levels
    exist to prevent. A clean close now satisfies 7.5; a missing frame is a
    note.

Verified both ways, and the cross-language CI job passes locally against the
modified client.

🤖 Generated with Claude Code

Every one of E7's 45 server logs ended the same way:

    1 violation(s)
        ConnectionClosedError: no close frame received or sent

The C++ client dropped the TCP connection without sending a WebSocket close
frame. It went unnoticed through every previous conformance run because in
all of them the *server* ran out of steps first and closed the connection
itself. E7 is the first experiment where the client finishes first - which
is also what a real env client does when it reaches its episode budget.

Three things were wrong, in three places.

**The client** did not send one. It now sends status 1000, per RFC 6455
section 5.5.1, before closing the socket.

**The specification had a hole.** Section 7 described four ways the server
closes and said nothing at all about the client stopping. New section 7.5:
a client may stop at any time and that is not an error - the server keeps no
state that outlives the connection - but it SHOULD send a close frame, so
that an operator reading the logs does not have to wonder whether a client
crashed.

**The conformance server was stricter than the specification.** It counted
any client disconnect as a violation, including a clean one. That is exactly
the failure its two severity levels exist to prevent, and it took a real
experiment to expose it. A clean close now satisfies 7.5; a missing close
frame is a note, which is the level a SHOULD deserves.

Verified both ways: the new client reports `ok 7.5` and no violations, and
the previous binary, kept for the comparison, reports `note 7.5` and still
no violations. The cross-language CI job passes locally against the modified
client, ldd allowlist included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tactino
tactino merged commit a81f778 into main Sep 11, 2026
3 checks passed
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