Skip to content

refactor(server): centralize WebSocket close reasons in an enum#9981

Merged
kirangadhave merged 1 commit into
mainfrom
kg/centralize-websocket-close-reasons
Jun 24, 2026
Merged

refactor(server): centralize WebSocket close reasons in an enum#9981
kirangadhave merged 1 commit into
mainfrom
kg/centralize-websocket-close-reasons

Conversation

@kirangadhave

@kirangadhave kirangadhave commented Jun 24, 2026

Copy link
Copy Markdown
Member

📝 Summary

The MARIMO_* WebSocket close-frame strings were duplicated as inline literals across ws_endpoint.py, ws/ws_connection_validator.py, ws/ws_session_connector.py, and terminal.py, with the frontend redeclaring a parallel CloseReason union. 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 existing WebSocketCodes in marimo/_server/codes.py, since the close code and close reason are passed together at every call site. The str subclass lets members pass straight into websocket.close() with no .value access. 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_ID was 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 by MARIMO_NO_SESSION_ID and MARIMO_NO_FILE_KEY. Both were orphaned handlers; the existing unknown-reason warning branch already covers anything the backend might newly emit.

MARIMO_TRANSPORT_EXHAUSTED stays frontend-synthesized and out of the backend enum.

Closes MO-6222

Review in cubic

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.
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jun 24, 2026 8:18pm

Request Review

@kirangadhave kirangadhave marked this pull request as ready for review June 24, 2026 20:17
Copilot AI review requested due to automatic review settings June 24, 2026 20:17
@kirangadhave kirangadhave added the internal A refactor or improvement that is not user facing label Jun 24, 2026
@kirangadhave kirangadhave requested a review from mscolnick June 24, 2026 20:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 WebSocketCloseReason enum in marimo/_server/codes.py and 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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Loading

Re-trigger cubic

@kirangadhave kirangadhave merged commit d04af98 into main Jun 24, 2026
49 of 51 checks passed
@kirangadhave kirangadhave deleted the kg/centralize-websocket-close-reasons branch June 24, 2026 20:34
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.11-dev24

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal A refactor or improvement that is not user facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants