From a6e4d482f1fcd63465dd9c89e60ee7dfc8f33594 Mon Sep 17 00:00:00 2001 From: Brian Cooper Date: Fri, 10 Jul 2026 23:51:07 -0600 Subject: [PATCH] fix(graphql): guard the 401 sign-out path in graphqlFetch The catch block did `error.response.status === 401 -> window.location.href = "/"` without checking `error.response` exists and without a clean sign-out. Align on the herald/vortex pattern: guard the response, sign out on a genuine 401 OR an UNAUTHENTICATED error code, clear the session server-side, then redirect. --- src/lib/graphql/graphqlFetch.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/lib/graphql/graphqlFetch.ts b/src/lib/graphql/graphqlFetch.ts index c988b67f..81aa2096 100644 --- a/src/lib/graphql/graphqlFetch.ts +++ b/src/lib/graphql/graphqlFetch.ts @@ -50,13 +50,25 @@ export const graphqlFetch = }, }); } catch (error) { - // Redirect to re-auth on 401 (expired/invalid token) + // Only sign out on a genuine 401 / UNAUTHENTICATED from the server. + // Network errors (aborted requests, timeouts during page transitions) are + // not ClientErrors and must never trigger sign-out; guarding `response` + // also avoids throwing a TypeError over the original error. if ( error instanceof ClientError && - error.response.status === 401 && - typeof window !== "undefined" + typeof window !== "undefined" && + error.response ) { - window.location.href = "/"; + const isHttp401 = error.response.status === 401; + const isUnauthenticated = error.response.errors?.some( + (e) => e.extensions?.code === "UNAUTHENTICATED", + ); + + if (isHttp401 || isUnauthenticated) { + await fetch("/api/auth/sign-out", { method: "POST" }); + window.location.href = "/"; + return new Promise(() => {}) as Promise; + } } throw error;