{"record":{"id":"54197226afa0c029","repo":"slymnoyann/hey-1","slug":"missing-tokens-in-refresh-response","errorCode":null,"errorMessage":"Missing tokens in refresh response","messagePattern":"Missing tokens in refresh response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/helpers/tokenManager.ts","lineNumber":29,"sourceCode":"  try {\n    for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n      const { data } = await apolloClient.mutate<RefreshMutation>({\n        mutation: RefreshDocument,\n        variables: { request: { refreshToken } }\n      });\n\n      const refreshResult = data?.refresh;\n\n      if (!refreshResult) {\n        throw new Error(\"No response from refresh\");\n      }\n\n      if (refreshResult.__typename === \"AuthenticationTokens\") {\n        const { accessToken: newAccessToken, refreshToken: newRefreshToken } =\n          refreshResult;\n\n        if (!newAccessToken || !newRefreshToken) {\n          throw new Error(\"Missing tokens in refresh response\");\n        }\n\n        signIn({\n          accessToken: newAccessToken,\n          refreshToken: newRefreshToken\n        });\n\n        return newAccessToken;\n      }\n\n      if (refreshResult.__typename === \"ForbiddenError\") {\n        signOut();\n        throw new Error(\"Refresh token is invalid or expired\");\n      }\n\n      if (attempt < MAX_RETRIES - 1) {\n        await new Promise((resolve) =>\n          setTimeout(resolve, 2 ** attempt * 1000)","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/slymnoyann/hey-1/blob/88c8f9d55340d57a37846ff72f55c2c604e3a566/src/helpers/tokenManager.ts#L11-L47","documentation":"Thrown when the refresh mutation does return an AuthenticationTokens payload, but either accessToken or refreshToken within it is empty/undefined. The server acknowledged the refresh request and typed the response as tokens, yet the actual credentials are missing, so signIn cannot be performed safely.","triggerScenarios":"API returns { __typename: 'AuthenticationTokens', accessToken: null } or omits refreshToken; a schema change making one token field nullable; or a partially successful server-side session regeneration where one token fails to mint.","commonSituations":"Backend version mismatch after a deploy that altered the refresh response shape, server-side JWT signing failure producing null fields, or a mocked/test server returning an incomplete AuthenticationTokens object.","solutions":["Inspect the raw refresh response in the network tab to confirm which token field is missing","Align the client mutation selection set with the current API schema (both accessToken and refreshToken selected)","If the server is yours, fix the resolver to always return both tokens or return an error typename instead","Treat this as a hard auth failure: signOut() before throwing so the user is not stuck in a broken session"],"exampleFix":"// before\nif (!newAccessToken || !newRefreshToken) {\n  throw new Error(\"Missing tokens in refresh response\");\n}\n\n// after\nif (!newAccessToken || !newRefreshToken) {\n  signOut();\n  throw new Error(\"Missing tokens in refresh response\");\n}","handlingStrategy":"type-guard","validationCode":"// nothing caller-side can validate pre-flight beyond having a well-formed refresh token\nif (!refreshToken) signOut();","typeGuard":"const isCompleteTokens = (r: unknown): r is { __typename: \"AuthenticationTokens\"; accessToken: string; refreshToken: string } =>\n  typeof r === \"object\" && r !== null &&\n  r.__typename === \"AuthenticationTokens\" &&\n  typeof (r as { accessToken?: unknown }).accessToken === \"string\" &&\n  typeof (r as { refreshToken?: unknown }).refreshToken === \"string\";","tryCatchPattern":"try {\n  await refreshTokens(refreshToken);\n} catch (e) {\n  if (e instanceof Error && e.message === \"Missing tokens in refresh response\") {\n    signOut();\n    window.location.href = \"/login\";\n  }\n  throw e;\n}","preventionTips":["Keep the client mutation selection set in sync with the API schema","Sign out on this specific error to avoid a half-authenticated session","Contract-test the refresh mutation response shape in CI"],"tags":["authentication","graphql","token-refresh","validation"],"backgroundTag":"refresh-token-failure","analyzedSha":"88c8f9d55340d57a37846ff72f55c2c604e3a566","analyzedAt":"2026-08-28T16:20:43.640Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}