{"record":{"id":"5f83e2bdfa3c896f","repo":"chatboxai/chatbox","slug":"failed-to-refresh-token-missing-tokens-in-respons","errorCode":null,"errorMessage":"Failed to refresh token: missing tokens in response headers","messagePattern":"Failed to refresh token: missing tokens in response headers","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/renderer/packages/remote.ts","lineNumber":960,"sourceCode":"      },\n    },\n    {\n      parseChatboxRemoteError: true,\n      retry: 2,\n    }\n  )\n  const json: Response = await res.json()\n  // log.info('✅ refreshAccessToken response', json)\n\n  const accessToken = res.headers.get('x-chatbox-access-token')\n  const refreshToken = res.headers.get('x-chatbox-refresh-token')\n\n  if (!accessToken || !refreshToken) {\n    log.error('❌ Missing tokens in response headers:', {\n      accessToken: accessToken ? 'present' : 'missing',\n      refreshToken: refreshToken ? 'present' : 'missing',\n    })\n    throw new Error('Failed to refresh token: missing tokens in response headers')\n  }\n\n  return {\n    accessToken,\n    refreshToken,\n  }\n}\n\nexport async function getUserProfile() {\n  type Response = {\n    data: {\n      email: string\n      id: string\n      created_at: string\n    }\n  }\n  const afetch = await getAuthenticatedAfetch()\n  const res = await afetch(","sourceCodeStart":942,"sourceCodeEnd":978,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/remote.ts#L942-L978","documentation":"Thrown by the token-refresh routine when the refresh response is missing the x-chatbox-access-token or x-chatbox-refresh-token response header. The app exchanges a refresh token for a new token pair via custom headers (not the JSON body), so absent headers mean the refresh contract broke. A log line records which header is missing.","triggerScenarios":"The refresh endpoint returns 2xx but without the expected x-chatbox-access-token / x-chatbox-refresh-token headers. Triggered whenever either header is null after `res.headers.get(...)`.","commonSituations":"A proxy/CDN strips custom x- headers; the backend deployed a change that moved tokens into the body or renamed headers; CORS exposure policy hides custom headers from the browser; a session expired server-side and the endpoint returned success without tokens.","solutions":["Check the server log/error to confirm the refresh endpoint is issuing both headers.","If behind a proxy/CDN, ensure x-chatbox-* headers are passed through and exposed via CORS (Access-Control-Expose-Headers).","Verify the user's refresh token is still valid — a revoked session may yield a tokenless success.","Confirm backend/header contract version matches the client."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// cannot fully validate headers pre-call, but ensure session is fresh:\nif (!authInfoStore.getState().refreshToken) {\n  // force re-login instead of attempting refresh\n}","typeGuard":"function hasTokenHeaders(res: Response): boolean {\n  return !!res.headers.get('x-chatbox-access-token') && !!res.headers.get('x-chatbox-refresh-token')\n}","tryCatchPattern":"try {\n  await refreshAccessToken()\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Failed to refresh token: missing tokens')) {\n    // force re-authentication; clear stale refresh token\n  }\n}","preventionTips":["Ensure the network path passes x-chatbox-* headers (proxy/CDN/CORS exposure).","Log out users cleanly when refresh fails rather than retrying in a loop.","Keep client and backend header contracts version-aligned."],"tags":["auth","network","token","headers"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}