{"record":{"id":"e560398d46b0e3b4","repo":"thedotmack/claude-mem","slug":"refusing-to-inject-expired-claude-code-oauth-token","errorCode":null,"errorMessage":"Refusing to inject expired CLAUDE_CODE_OAUTH_TOKEN: ${result.reason}. Re-login via Claude Desktop to refresh.","messagePattern":"Refusing to inject expired CLAUDE_CODE_OAUTH_TOKEN: (.+?)\\. Re-login via Claude Desktop to refresh\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/shared/EnvManager.ts","lineNumber":276,"sourceCode":"      'OAUTH',\n      'OAuth token read failed unexpectedly; proceeding without token',\n      {},\n      error instanceof Error ? error : new Error(String(error)),\n    );\n    return isolatedEnv;\n  }\n\n  switch (result.kind) {\n    case 'present':\n      isolatedEnv.CLAUDE_CODE_OAUTH_TOKEN = result.token;\n      logger.info('OAUTH', 'Injected fresh CLAUDE_CODE_OAUTH_TOKEN at spawn-time', {\n        source: result.source,\n        expiresAt: result.expiresAt,\n      });\n      clearStaleMarker();\n      break;\n    case 'expired':\n      logger.warn(\n        'OAUTH',\n        `Refusing to inject expired CLAUDE_CODE_OAUTH_TOKEN: ${result.reason}. Re-login via Claude Desktop to refresh.`,\n        { expiresAt: result.expiresAt },\n      );\n      writeStaleMarker(result.reason);\n      break;\n    case 'absent':\n      logger.debug('OAUTH', `No OAuth token available: ${result.reason}`);\n      // Token is absent — any prior stale-marker would have been written\n      // when the token was expired, but is no longer accurate now that the\n      // token is gone. Clear it so the session-start hook stops surfacing\n      // a stale \"expired token, re-login\" warning (CodeRabbit review on PR\n      // #2282).\n      clearStaleMarker();\n      break;\n  }\n\n  return isolatedEnv;","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/shared/EnvManager.ts#L258-L294","documentation":"While assembling the isolated environment for a child spawn, EnvManager resolves the Claude OAuth token. If the token is present but expired (result.kind === 'expired'), it refuses to inject CLAUDE_CODE_OAUTH_TOKEN, logs the reason and expiresAt, and writes a stale-marker file so the session-start hook can surface a re-login prompt to the user. The child runs without the token.","triggerScenarios":"The OAuth credential store holds a token whose expiresAt is in the past at spawn time; the 'expired' switch branch fires and writeStaleMarker(result.reason) records it.","commonSituations":"Claude Desktop login older than the token lifetime; user logged out or switched accounts; machine slept past expiry and resumed a long-lived worker.","solutions":["Re-login via Claude Desktop to mint a fresh token — the marker clears automatically on the next fresh injection.","If the stale warning persists after re-login, verify the credential store Claude Desktop actually writes to is the one being read.","Recurring fast expiry: check system clock skew, since expiry is wall-clock based."],"exampleFix":"// before\nisolatedEnv.CLAUDE_CODE_OAUTH_TOKEN = process.env.CLAUDE_CODE_OAUTH_TOKEN; // may be expired\n\n// after\nconst result = resolveOAuthToken();\nif (result.kind === 'present') {\n  isolatedEnv.CLAUDE_CODE_OAUTH_TOKEN = result.token;\n} else if (result.kind === 'expired') {\n  logger.warn('OAUTH', `Refusing to inject expired CLAUDE_CODE_OAUTH_TOKEN: ${result.reason}. Re-login via Claude Desktop to refresh.`, { expiresAt: result.expiresAt });\n  writeStaleMarker(result.reason);\n}","handlingStrategy":"validation","validationCode":"const result = resolveOAuthToken();\nif (result.kind === 'expired') {\n  // surface re-login to the user instead of spawning unauthenticated\n  return promptRelogin(result.reason);\n}","typeGuard":"type TokenResult =\n  | { kind: 'present'; token: string; source: string; expiresAt: number }\n  | { kind: 'expired'; reason: string; expiresAt: number }\n  | { kind: 'absent'; reason: string };\n\nconst isExpiredToken = (r: TokenResult): r is Extract<TokenResult, { kind: 'expired' }> =>\n  r.kind === 'expired';","tryCatchPattern":null,"preventionTips":["Re-login via Claude Desktop as soon as the stale-token warning appears; don't wait for spawn failures.","Never hardcode or export a stale CLAUDE_CODE_OAUTH_TOKEN by hand — let the resolver inject fresh tokens.","Watch expiresAt in logs to anticipate refresh needs on long-lived machines."],"tags":["oauth","token-expired","env","spawn","claude-mem"],"backgroundTag":"oauth-token-expired","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}