Found while working on #16935 (flattening the type: "error" fences on the same page onto ErrorMessageSchema). That card's dispatch deliberately fenced the type: "auth_error" fence at :220 out of scope and asked for it to be measured and reported rather than rewritten — measuring it turned up a wider, separate defect, filed here.
Measured on origin/main at fd5cff209f. Every line number below is from that tree.
The declared contract
packages/spec/src/api/websocket.zod.ts:38 declares the closed vocabulary of WebSocket message types, and every message schema on that tree pins its type to a member of it:
export const WebSocketMessageType = z.enum([
'subscribe', 'unsubscribe', 'event', 'ping', 'pong',
'ack', 'error', 'presence', 'cursor', 'edit',
]);
BaseWebSocketMessage (:269) types its type field as that enum, so a message whose type is not a member of it does not parse against any schema in the file.
What the page teaches instead
content/docs/protocol/kernel/realtime-protocol.mdx documents a connection handshake and a subscribe acknowledgement built entirely out of message types that are not in that enum:
| site |
type in the fence |
declared in WebSocketMessageType? |
:202 (JSON fence) |
auth |
no |
:210 (JSON fence) |
auth_success |
no |
:220 (JSON fence) |
auth_error |
no |
:258 (JSON fence) |
subscribed |
no |
:176, :590, :1012 (JS snippets) |
auth |
no |
The same fences also carry top-level fields no schema declares — token, user_id, session_id, expires_at on the auth pair, and subscription_id / object / events / filter on the subscribe pair.
Read live against the built spec, so this is not a grep artefact:
error DECLARED
auth_error NOT DECLARED
auth NOT DECLARED
auth_success NOT DECLARED
subscribed NOT DECLARED
(WebSocketMessageType.safeParse(t) over packages/spec/dist/api/index.mjs; error is the positive control on the same corpus and the same call.)
Producer readings on the same tree, each with a positive control:
| reading |
result |
positive control |
auth_error in packages/** |
1 line, and it is not a hit: packages/spec/src/identity/protocol.ts:136 is OAUTH_ERROR: 'oauth_error', an OAuth error-code constant matched as a substring |
ErrorMessageSchema in packages/** returns 11 lines across 6 files |
auth_success in the repo, excluding content/docs |
0 |
same control as above |
Why this is a defect and not a style note
Nothing on this tree emits any WebSocket message at all — packages/spec/CHANGELOG.md records that "no WebSocket server is mounted (#2462); the protocol is a future wire contract", and the page's own implementation-status callout says the transport is unwired. So the page is the only authoring surface for this protocol, and WebSocketMessageType is the only declaration of it. A client written from the handshake section sends {"type": "auth", ...}, which the one schema governing that field rejects; a client written from the subscribe section waits for {"type": "subscribed"}, which nothing can ever send. Copying the examples fails against the declared contract.
Two readings, and they lead to opposite edits
Unlike #16935's envelope half — where both readings agreed, because ErrorMessageSchema is flat whether or not the capability ever ships — this one genuinely does not settle itself:
- A — the page is wrong. The handshake is not part of the declared protocol; rewrite or remove those fences, and express authentication in terms the enum can carry.
- B — the enum is incomplete. An auth handshake and a subscribe acknowledgement are real parts of a WebSocket wire protocol that the spec simply has not declared yet; the enum and the message schemas should grow
auth / auth_success / auth_error / subscribed before the transport lands.
B is a protocol change and needs a decision — which is exactly why #16935's dispatch ruled :220 out of that card's scope: rewriting the fence to type: "error" there would have been changing the protocol under cover of a documentation fix.
Not fixed here
Left byte-unchanged by the #16935 PR. Nothing gates it either: these are JSON fences, so no type-check compares a documented realtime message against the declared enum — that gap is the class (B) population of #17127, whose census should count this page.
Related: #16935, #17127, #16937.
Generated by Claude Code
Found while working on #16935 (flattening the
type: "error"fences on the same page ontoErrorMessageSchema). That card's dispatch deliberately fenced thetype: "auth_error"fence at:220out of scope and asked for it to be measured and reported rather than rewritten — measuring it turned up a wider, separate defect, filed here.Measured on
origin/mainatfd5cff209f. Every line number below is from that tree.The declared contract
packages/spec/src/api/websocket.zod.ts:38declares the closed vocabulary of WebSocket message types, and every message schema on that tree pins itstypeto a member of it:BaseWebSocketMessage(:269) types itstypefield as that enum, so a message whosetypeis not a member of it does not parse against any schema in the file.What the page teaches instead
content/docs/protocol/kernel/realtime-protocol.mdxdocuments a connection handshake and a subscribe acknowledgement built entirely out of message types that are not in that enum:typein the fenceWebSocketMessageType?:202(JSON fence)auth:210(JSON fence)auth_success:220(JSON fence)auth_error:258(JSON fence)subscribed:176,:590,:1012(JS snippets)authThe same fences also carry top-level fields no schema declares —
token,user_id,session_id,expires_aton the auth pair, andsubscription_id/object/events/filteron the subscribe pair.Read live against the built spec, so this is not a grep artefact:
(
WebSocketMessageType.safeParse(t)overpackages/spec/dist/api/index.mjs;erroris the positive control on the same corpus and the same call.)Producer readings on the same tree, each with a positive control:
auth_errorinpackages/**packages/spec/src/identity/protocol.ts:136isOAUTH_ERROR: 'oauth_error', an OAuth error-code constant matched as a substringErrorMessageSchemainpackages/**returns 11 lines across 6 filesauth_successin the repo, excludingcontent/docsWhy this is a defect and not a style note
Nothing on this tree emits any WebSocket message at all —
packages/spec/CHANGELOG.mdrecords that "no WebSocket server is mounted (#2462); the protocol is a future wire contract", and the page's own implementation-status callout says the transport is unwired. So the page is the only authoring surface for this protocol, andWebSocketMessageTypeis the only declaration of it. A client written from the handshake section sends{"type": "auth", ...}, which the one schema governing that field rejects; a client written from the subscribe section waits for{"type": "subscribed"}, which nothing can ever send. Copying the examples fails against the declared contract.Two readings, and they lead to opposite edits
Unlike #16935's envelope half — where both readings agreed, because
ErrorMessageSchemais flat whether or not the capability ever ships — this one genuinely does not settle itself:auth/auth_success/auth_error/subscribedbefore the transport lands.B is a protocol change and needs a decision — which is exactly why #16935's dispatch ruled
:220out of that card's scope: rewriting the fence totype: "error"there would have been changing the protocol under cover of a documentation fix.Not fixed here
Left byte-unchanged by the #16935 PR. Nothing gates it either: these are JSON fences, so no type-check compares a documented realtime message against the declared enum — that gap is the class (B) population of #17127, whose census should count this page.
Related: #16935, #17127, #16937.
Generated by Claude Code