From 52c8edcf93ef1f8a8ff9b1f84319f09a2444c149 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sun, 1 Feb 2026 12:27:01 -0600 Subject: [PATCH] Fix WebSocket game:join to emit game:state event Backend was returning the state but not emitting the game:state event that the frontend listens for. Added explicit emit call to send game:state to the client after successful join. Co-Authored-By: Claude Sonnet 4.5 --- backend/app/socketio/game_namespace.py | 14 ++++++++++++++ frontend/src/api/client.ts | 15 ++++++++++++++- frontend/src/api/types.ts | 3 ++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/backend/app/socketio/game_namespace.py b/backend/app/socketio/game_namespace.py index 20d27f3..8cd37b8 100644 --- a/backend/app/socketio/game_namespace.py +++ b/backend/app/socketio/game_namespace.py @@ -177,6 +177,20 @@ class GameNamespaceHandler: response["turn_timeout_seconds"] = result.turn_timeout_seconds response["turn_deadline"] = result.turn_deadline + # Emit game:state event to the client + if result.visible_state: + await sio.emit( + "game:state", + { + "game_id": game_id, + "state": response["state"], + "is_your_turn": response["is_your_turn"], + "game_over": response["game_over"], + }, + to=sid, + namespace="/game", + ) + logger.info(f"Player {user_id} joined game {game_id}") return response diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index a219526..16bef31 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -43,7 +43,20 @@ async function parseErrorResponse(response: Response): Promise { try { const data: ErrorResponse = await response.json() - detail = data.detail || data.message + + // Handle Pydantic validation errors (422) - detail is an array + if (Array.isArray(data.detail)) { + // Extract error messages from validation error array + detail = data.detail + .map((err: any) => { + const field = err.loc ? err.loc.join('.') : 'unknown' + return `${field}: ${err.msg || 'Invalid value'}` + }) + .join('; ') + } else { + detail = data.detail || data.message + } + code = data.code } catch { // Response body is not JSON or empty diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index 607d98e..57ca69a 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -92,9 +92,10 @@ export interface PaginatedResponse { /** * Backend error response shape. + * For validation errors (422), detail is an array of validation error objects. */ export interface ErrorResponse { - detail?: string + detail?: string | Array<{ loc: string[]; msg: string; type: string }> message?: string code?: string }