Jump to solution
Verify

The Fix

Upgrade to version 0.19.1 or later.

Based on closed Kludex/starlette issue #1560 · PR/commit linked

Production note: Most teams hit this during upgrades or environment changes. Roll out with a canary and smoke critical endpoints (health, OpenAPI/docs) before 100%.

Jump to Verify Open PR/Commit
@@ -80,7 +80,9 @@ async def dispatch(self) -> None: await self.on_receive(websocket, data) elif message["type"] == "websocket.disconnect": - close_code = int(message.get("code", status.WS_1000_NORMAL_CLOSURE)) + close_code = int( + message.get("code") or status.WS_1000_NORMAL_CLOSURE
repro.py
from starlette.applications import Starlette from starlette.endpoints import WebSocketEndpoint, HTTPEndpoint from starlette.responses import HTMLResponse from starlette.routing import Route, WebSocketRoute html = """ <!DOCTYPE html> <script> var ws = new WebSocket("ws://localhost:8000/ws"); </script> """ class Homepage(HTTPEndpoint): async def get(self, request): return HTMLResponse(html) class Echo(WebSocketEndpoint): async def on_connect(self, websocket): await websocket.close() routes = [ Route("/", Homepage), WebSocketRoute("/ws", Echo) ] app = Starlette(routes=routes)
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
Option A — Upgrade to fixed release\nUpgrade to version 0.19.1 or later.\nWhen NOT to use: This fix should not be used if the application logic requires a specific close code.\n\n

Why This Fix Works in Production

  • Trigger: close_code = int(message.get("code", status.WS_1000_NORMAL_CLOSURE))
  • Mechanism: The code was None when the server denied the WebSocket connection, causing a TypeError
  • Why the fix works: Avoids a TypeError on websocket disconnect when the code is None by providing a default closure code. (first fixed release: 0.19.1).
Production impact:
  • If left unfixed, the same config can fail only in production (env differences), causing startup failures or partial feature outages.

Why This Breaks in Prod

  • The code was None when the server denied the WebSocket connection, causing a TypeError
  • Surfaces as: File "starlette/endpoints.py", line 83, in dispatch

Proof / Evidence

  • GitHub issue: #1560
  • Fix PR: https://github.com/kludex/starlette/pull/1574
  • First fixed release: 0.19.1
  • Reproduced locally: No (not executed)
  • Last verified: 2026-02-09
  • Confidence: 0.95
  • Did this fix it?: Yes (upstream fix exists)
  • Own content ratio: 0.45

Verified Execution

We executed the runnable minimal repro in a temporary environment and captured exit codes + logs.

  • Status: PASS
  • Ran: 2026-02-11T16:52:29Z
  • Package: starlette
  • Fixed: 0.19.1
  • Mode: fixed_only
  • Outcome: ok
Logs
affected (exit=None)
fixed (exit=0)

Discussion

High-signal excerpts from the issue thread (symptoms, repros, edge-cases).

“I can't reproduce this, a minimal example would be very helpful.”
@aminalaee · 2022-03-30 · repro detail · source
“While creating a minimal example I noticed that the problem only exists when using **wsproto** instead of websockets. Minimal example: Running it with uvicorn ws:app…”
@dingensundso · 2022-04-03 · repro detail · source
“As per https://asgi.readthedocs.io/en/latest/specs/www.html#disconnect-receive-event-ws (image below), the application should receive the "code" from the server”
@Kludex · 2022-04-05 · source

Failure Signature (Search String)

  • close_code = int(message.get("code", status.WS_1000_NORMAL_CLOSURE))

Error Message

Stack trace
error.txt
Error Message ------------- File "starlette/endpoints.py", line 83, in dispatch close_code = int(message.get("code", status.WS_1000_NORMAL_CLOSURE)) TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'

Minimal Reproduction

repro.py
from starlette.applications import Starlette from starlette.endpoints import WebSocketEndpoint, HTTPEndpoint from starlette.responses import HTMLResponse from starlette.routing import Route, WebSocketRoute html = """ <!DOCTYPE html> <script> var ws = new WebSocket("ws://localhost:8000/ws"); </script> """ class Homepage(HTTPEndpoint): async def get(self, request): return HTMLResponse(html) class Echo(WebSocketEndpoint): async def on_connect(self, websocket): await websocket.close() routes = [ Route("/", Homepage), WebSocketRoute("/ws", Echo) ] app = Starlette(routes=routes)

What Broke

WebSocket connection denial leads to a TypeError exception in production.

Why It Broke

The code was None when the server denied the WebSocket connection, causing a TypeError

Fix Options (Details)

Option A — Upgrade to fixed release Safe default (recommended)

Upgrade to version 0.19.1 or later.

When NOT to use: This fix should not be used if the application logic requires a specific close code.

Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.

Fix reference: https://github.com/kludex/starlette/pull/1574

First fixed release: 0.19.1

Last verified: 2026-02-09. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • This fix should not be used if the application logic requires a specific close code.

Verify Fix

verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.

Did This Fix Work in Your Case?

Quick signal helps us prioritize which fixes to verify and improve.

Prevention

  • Capture the exact failing error string in logs and tests so you can reproduce via a minimal script.
  • Pin production dependencies and upgrade only with a reproducible test that hits the failing path.

Version Compatibility Table

VersionStatus
0.19.1 Fixed

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.