Fix two production crashes and bump to v3.2.0

1. Fix IndexError in current_player() when player leaves mid-game
   - remove_player() now adjusts current_player_index after popping
   - current_player() has safety bounds check as defensive fallback

2. Fix AssertionError in StaticFiles catching WebSocket upgrades
   - Wrap static file mount to reject non-HTTP requests gracefully
   - Starlette's StaticFiles asserts scope["type"] == "http"

Both crashes were observed in production on 2026-02-28 during a
multi-player session. The IndexError cascaded into reconnection
attempts that hit the StaticFiles assertion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-03-05 20:30:08 -05:00
parent 7f0f580631
commit a8b521f7f7
2 changed files with 26 additions and 5 deletions

View File

@@ -431,7 +431,7 @@ async def _close_all_websockets():
app = FastAPI(
title="Golf Card Game",
debug=config.DEBUG,
version="3.1.6",
version="3.2.0",
lifespan=lifespan,
)
@@ -943,7 +943,18 @@ if os.path.exists(client_path):
return FileResponse(os.path.join(client_path, "index.html"))
# Mount static files for everything else (JS, CSS, SVG, etc.)
app.mount("/", StaticFiles(directory=client_path), name="static")
# Wrap StaticFiles to reject WebSocket requests gracefully instead of
# crashing with AssertionError (starlette asserts scope["type"] == "http").
static_files = StaticFiles(directory=client_path)
async def safe_static_files(scope, receive, send):
if scope["type"] != "http":
if scope["type"] == "websocket":
await send({"type": "websocket.close", "code": 1000})
return
await static_files(scope, receive, send)
app.mount("/", safe_static_files, name="static")
def run():