From d21ff5d977cadbe3b73af190627000ffcedde351 Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Sat, 28 Feb 2026 09:48:42 -0800 Subject: [PATCH] fix: revalidate draft room when Clerk JWT expires at load time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clerk's short-lived JWTs (~1 min) can expire between navigations. Server-side clerkMiddleware can't refresh them for client-side loader fetches, so getAuth() returns userId=null — causing userTeam=undefined, the queue button to disappear, and the tab to default to "board". Added a useEffect that detects the mismatch: if the Clerk client SDK has a valid userId but the loader ran without one, trigger revalidate() so the loader re-runs with a fresh token and returns correct userTeam/userQueue data. Co-Authored-By: Claude Sonnet 4.6 --- app/routes/leagues/$leagueId.draft.$seasonId.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index 4b89b0f..52846c1 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -1,4 +1,5 @@ import { useLoaderData, Link, useRevalidator } from "react-router"; +import { useAuth } from "@clerk/react-router"; import { eq, and, asc, desc, inArray } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; @@ -229,6 +230,7 @@ export default function DraftRoom() { ownerMap, } = useLoaderData(); const { revalidate, state: revalidatorState } = useRevalidator(); + const { userId: clerkUserId } = useAuth(); const { isConnected, connectionError, isReconnecting, reconnectCount, on, off } = useDraftSocket(season.id, userTeam?.id); const { permissionState: notificationsPermission, @@ -268,6 +270,15 @@ export default function DraftRoom() { } }, [reconnectCount, revalidate]); + // Guard against Clerk JWT expiry race: if the loader ran without a userId (expired + // short-lived JWT) but the Clerk client SDK has a valid session, revalidate so the + // loader re-runs with a fresh token and returns correct userTeam/userQueue data. + useEffect(() => { + if (clerkUserId && !currentUserId) { + revalidate(); + } + }, [clerkUserId, currentUserId, revalidate]); + // Track revalidation lifecycle to sync local state from fresh loader data. // // The problem: `picks` and `currentPick` are local state initialized once