Matchmaking returns "Session not found" (401) for a valid JWT after a transient MQTT disconnect, with no recovery path
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 75/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Tranquilla
- Stack tecnologico
- typescript
- Ambito
- api, authentication, backend
Direzione di ricerca
Inizia in src/features/matchmaking/matchmaking.route.ts, nella ricerca rigorosa di getSession, quindi confrontala con ensureSession in src/features/auth/auth.service.ts. Riproduci la richiesta con sessione mancante descritta nell’issue e verifica che un JWT valido possa recuperare una sessione supportata dal database invece di ricevere Session not found (401).
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Matchmaking returns "Session not found" (401) for a player with a valid JWT, with no recovery path
Summary
A player who is correctly authenticated (valid JWT) can be permanently locked out of matchmaking with Session not found (401) after a single transient MQTT disconnect. Retrying does not help, because nothing recreates the in-memory session and the still-valid JWT means the client never re-authenticates. The only workaround is a full client restart.
Root cause
The in-memory session and the stateless JWT have decoupled lifetimes, and the matchmaking route is the only flow that requires the session strictly instead of self-healing.
-
Sessions are in-memory only.
src/state/index.ts—export const sessions = new Map<string, PlayerSession>(). No persistence, no rehydration on startup. -
JWT auth never checks for a session.
src/middleware/authenticate.tsonly runsverifyJwt(token)and setsreq.player. A valid token passes even when the session is gone. -
A non-lobby disconnect reaps the session immediately.
src/features/emqx/emqx.route.ts:async function releasePlayerLobbyOrSession(clientid) { const session = getSession(clientid) if (!session) return leaveAllQueues(clientid) if (session.lobbyCode) { await startGracePeriod(clientid) // lobby members get a grace period } else { removeSession(clientid) // everyone else is removed on the spot } }The MQTT
clientidis theplayerId(the client sendsplayer_idas the MQTT client id), sogetSession(clientid)/removeSession(clientid)resolve the real session. A player who is matchmaking — and therefore not yet in a lobby — has their session deleted the instant EMQX reportsclient.disconnected. -
Matchmaking requires the session strictly.
src/features/matchmaking/matchmaking.route.ts:const session = getSession(req.player.playerId) if (!session) throw new AppError('Session not found', 401)By contrast, ~5 other endpoints call
ensureSession()(src/features/auth/auth.service.ts), which rebuilds the session from the DB when it's missing. Matchmaking is the asymmetric one that does not self-heal.
Reproduction
- Authenticate (Steam) — session created, MQTT connected.
- Drop the MQTT connection briefly (ordinary network blip). The client default is
reconnect = false, so it does not auto-reconnect. - EMQX fires
client.disconnected; since the player isn't in a lobby, the server callsremoveSessionimmediately. - Click matchmake →
POST /api/matchmaking/queuewith the still-valid JWT →getSessionreturns nothing →Session not found(401). - Retrying never recovers: the JWT is still valid, so the client doesn't re-auth, and nothing else recreates the session.
Impact
A single transient disconnect strands an authenticated player. From the user's side it looks like matchmaking is simply broken; the only fix they have is to fully quit and relaunch the game (which forces a fresh /auth and recreates the session).
Suggested fixes (in order of smallest blast radius)
- Make matchmaking self-heal like the other routes — use
ensureSession(req.player.playerId)instead of strictgetSession. A DB-backed (Steam-authed) player would transparently get their session rebuilt instead of a 401. This is the minimal, consistent fix. - Don't immediately reap non-lobby sessions on disconnect — give them the same grace period lobby members get, so a brief blip doesn't destroy the session.
- (Client-side, optional) Treat a
401 Session not foundas "re-authenticate, then retry" rather than surfacing it as a terminal error.
Notes / unverified
The mechanism above is confirmed against the code end-to-end (in-memory map, stateless JWT, immediate non-lobby reap, strict matchmaking lookup, clientid == playerId). What is not independently confirmed is that a given user report was caused by this exact path versus another session-loss trigger (e.g. a server restart/redeploy wiping the in-memory map, or a grace-period expiry). All of those funnel into the same end state — valid JWT, no session, strict 401 — so the fixes apply regardless.
- Lingua principale
- TypeScript
- Stelle
- 13
- Fork
- 21
- Merge medio
- 3g 16h
- PR unite (30g)
- 6
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di Balatro-Multiplayer/BalatroMultiplayerAPI-Server
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 72/100
-
In Game Telemetry Aperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
Tutte le issue di Balatro-Multiplayer/BalatroMultiplayerAPI-Server
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
vercel-labs/just-bash#464 ·
-
looksLikeSlug() is ASCII-only, so non-Latin entity slugs (e.g. Korean) skip exact match and collapse Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
TanStack/tanstack.com#1293 ·