refactor(server): centralize WebSocket close reasons in an enum#9981
Merged
Conversation
Replace the MARIMO_* close-frame literals duplicated across the ws endpoints with a WebSocketCloseReason enum in codes.py, so a typo fails fast instead of silently hitting the frontend's retry path. Also drop two dead frontend reasons (MARIMO_WRONG_KERNEL_ID, MARIMO_MALFORMED_QUERY) that no backend emitter ever sent.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR centralizes backend WebSocket close-frame reason strings into a single WebSocketCloseReason(str, Enum) alongside existing WebSocketCodes, reducing duplication across server endpoints and making wire-reason strings easier to keep consistent with frontend handling.
Changes:
- Added
WebSocketCloseReasonenum inmarimo/_server/codes.pyand updated server endpoints to use it instead of inline"MARIMO_*"literals. - Added a Python test that pins each enum member’s wire value to prevent accidental changes.
- Removed two dead/orphaned frontend close reasons (
MARIMO_WRONG_KERNEL_ID,MARIMO_MALFORMED_QUERY) and updated related TS types/tests/UI comments accordingly.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/_server/test_codes.py | Adds a regression test pinning WebSocket close-reason wire strings. |
| marimo/_server/codes.py | Introduces WebSocketCloseReason enum colocated with WebSocketCodes. |
| marimo/_server/api/endpoints/ws/ws_session_connector.py | Replaces inline kiosk/session close reasons with enum members. |
| marimo/_server/api/endpoints/ws/ws_connection_validator.py | Replaces inline auth/param close reasons with enum members. |
| marimo/_server/api/endpoints/ws_endpoint.py | Replaces inline RTC/kernel close reasons with enum members. |
| marimo/_server/api/endpoints/terminal.py | Replaces inline unauthorized close reason with enum member. |
| frontend/src/core/websocket/useMarimoKernelConnection.tsx | Removes dead close reasons from the union and classifier switch. |
| frontend/src/core/websocket/types.ts | Removes unused MALFORMED_QUERY closed reason constant. |
| frontend/src/core/websocket/tests/useMarimoKernelConnection.test.ts | Updates tests to reflect removed close reasons. |
| frontend/src/components/editor/header/status.tsx | Updates comment describing which terminal reasons are non-recoverable. |
| frontend/src/components/editor/header/tests/status.test.tsx | Removes test coverage for removed MALFORMED_QUERY UI text. |
Contributor
There was a problem hiding this comment.
No issues found across 11 files
Architecture diagram
sequenceDiagram
participant Client as Frontend WebSocket
participant WS as WebSocket Endpoint
participant Validator as WS Connection Validator
participant Connector as WS Session Connector
participant Terminal as Terminal Endpoint
participant Codes as WebSocketCloseReason Enum
Note over Client,Codes: WebSocket Close Flow with Centralized Close Reasons
Client->>WS: WebSocket connection request
WS->>Validator: validate connection parameters
Validator->>Codes: WebSocketCloseReason.NO_SESSION_ID
Validator->>Client: close(1000, "MARIMO_NO_SESSION_ID")
alt No file key in query
Validator->>Codes: WebSocketCloseReason.NO_FILE_KEY
Validator->>Client: close(1000, "MARIMO_NO_FILE_KEY")
end
alt Unauthorized
Validator->>Codes: WebSocketCloseReason.UNAUTHORIZED
Validator->>Client: close(3000, "MARIMO_UNAUTHORIZED")
end
WS->>Connector: connect kiosk session
alt Kiosk not allowed
Connector->>Codes: WebSocketCloseReason.KIOSK_NOT_ALLOWED
Connector->>Client: close(1008, "MARIMO_KIOSK_NOT_ALLOWED")
else No session found
Connector->>Codes: WebSocketCloseReason.NO_SESSION
Connector->>Client: close(1000, "MARIMO_NO_SESSION")
end
alt Loro not installed
WS->>Codes: WebSocketCloseReason.LORO_NOT_INSTALLED
WS->>Client: close(1000, "MARIMO_LORO_NOT_INSTALLED")
else Session not found for RTC
WS->>Codes: WebSocketCloseReason.NOT_ALLOWED
WS->>Client: close(1008, "MARIMO_NOT_ALLOWED")
end
alt Kernel startup error
WS->>Codes: WebSocketCloseReason.KERNEL_STARTUP_ERROR
WS->>Client: close(1011, "MARIMO_KERNEL_STARTUP_ERROR")
end
Terminal->>Codes: WebSocketCloseReason.UNAUTHORIZED
Terminal->>Client: close(3000, "MARIMO_UNAUTHORIZED")
Note over Client: Frontend classifies close reason
Client->>Client: classifyCloseEvent(reason)
alt Terminal reasons
Client->>Client: Show status overlay with reason
else KERNEL_DISCONNECTED
Client->>Client: Allow reconnection
else Unknown reason
Client->>Client: Show generic error
end
Note over WS,Terminal: on_detach cleanup
WS->>Codes: WebSocketCloseReason.SHUTDOWN
WS->>Client: close(1000, "MARIMO_SHUTDOWN")</firstCommitMessage>
mscolnick
approved these changes
Jun 24, 2026
Contributor
|
🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.11-dev24 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 Summary
The
MARIMO_*WebSocket close-frame strings were duplicated as inline literals acrossws_endpoint.py,ws/ws_connection_validator.py,ws/ws_session_connector.py, andterminal.py, with the frontend redeclaring a parallelCloseReasonunion. Keeping them aligned relied on convention, and a typo at any call site would silently fall through to the frontend's retry path rather than failing fast.This defines
WebSocketCloseReason(str, Enum)alongside the existingWebSocketCodesinmarimo/_server/codes.py, since the close code and close reason are passed together at every call site. Thestrsubclass lets members pass straight intowebsocket.close()with no.valueaccess. A new test pins each member's wire string so a rename cannot change the bytes the frontend matches against.It also removes two dead frontend close reasons.
MARIMO_WRONG_KERNEL_IDwas frontend-only since the initial commit and never had a backend emitter.MARIMO_MALFORMED_QUERY's backend emitter was dropped in the Starlette migration, its role superseded byMARIMO_NO_SESSION_IDandMARIMO_NO_FILE_KEY. Both were orphaned handlers; the existing unknown-reason warning branch already covers anything the backend might newly emit.MARIMO_TRANSPORT_EXHAUSTEDstays frontend-synthesized and out of the backend enum.Closes MO-6222