{"record":{"id":"b76e247cc4a815b5","repo":"decolua/9router","slug":"token-validation-failed-error-message","errorCode":null,"errorMessage":"Token validation failed: ${error.message}","messagePattern":"Token validation failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/kiro.js","lineNumber":258,"sourceCode":"   */\n  async validateImportToken(refreshToken) {\n    // Validate token format\n    if (!refreshToken.startsWith(\"aorAAAAAG\")) {\n      throw new Error(\"Invalid token format. Token should start with aorAAAAAG...\");\n    }\n\n    // Try to refresh to validate\n    try {\n      const result = await this.refreshToken(refreshToken);\n      return {\n        accessToken: result.accessToken,\n        refreshToken: result.refreshToken || refreshToken,\n        profileArn: result.profileArn,\n        expiresIn: result.expiresIn,\n        authMethod: \"imported\",\n      };\n    } catch (error) {\n      throw new Error(`Token validation failed: ${error.message}`);\n    }\n  }\n\n  /**\n   * List available CodeWhisperer profiles for OAuth/IDC tokens and return the\n   * best-matching profileArn. API keys use the Amazon Q model catalog instead;\n   * ListAvailableProfiles does not support TokenType=API_KEY.\n   */\n  async listAvailableProfiles(accessToken, region = \"us-east-1\") {\n    assertValidAwsRegion(region);\n    const endpoint = `https://codewhisperer.${region}.amazonaws.com`;\n\n    const response = await fetch(endpoint, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/x-amz-json-1.0\",\n        \"x-amz-target\": \"AmazonCodeWhispererService.ListAvailableProfiles\",\n        \"Authorization\": `Bearer ${accessToken}`,","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/kiro.js#L240-L276","documentation":"Thrown by KiroService.validateImportToken when the token format passed the aorAAAAAG check but the subsequent refreshToken() probe failed. The original error's message is re-wrapped, so the root cause (usually the upstream body from errors 284/285) is preserved in the text. It means the pasted refresh token is well-formed but not actually usable — invalid, revoked, or expired.","triggerScenarios":"validateImportToken calls this.refreshToken(token) (social path, since no clientId/secret is passed) and it throws — invalid_grant, revoked token, or Kiro auth service returning non-2xx; the catch block rethrows as `Token validation failed: ${error.message}`.","commonSituations":"Importing a Kiro refresh token that was already rotated/revoked (e.g. exported from an old session); token from a revoked device authorization; Kiro service outage at import time; accidentally importing an AWS SSO refresh token that only works via the OIDC endpoint, not the social refreshToken endpoint.","solutions":["Read the wrapped message for the root cause: invalid/revoked tokens require re-auth in Kiro and a fresh export.","Export the refresh token again from a currently-logged-in Kiro session and retry immediately.","If the token came from an AWS SSO (device-flow) login, it may need the OIDC refresh path — check which auth method produced it.","Retry later if the embedded message indicates a server-side 5xx rather than an auth rejection."],"exampleFix":"// before: silent import retry with stale token\ntry { await svc.validateImportToken(old); } catch { await svc.validateImportToken(old); }\n// after: prompt re-export on failure\ncatch (e) {\n  if (/Invalid|revoked|expired/i.test(e.message)) promptUserToReExport();\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// format check first so only the refresh probe can fail\nif (!token.trim().startsWith('aorAAAAAG')) throw new Error('Not a Kiro refresh token');\n// optional liveness hint: token must be a plausible length\nif (token.trim().length < 30) throw new Error('Refresh token looks truncated');","typeGuard":"function isImportResult(r) { return typeof r?.accessToken === 'string' && r?.authMethod === 'imported'; }","tryCatchPattern":"try {\n  return await svc.validateImportToken(token);\n} catch (e) {\n  // e.message embeds the root cause from refreshToken — surface it verbatim\n  if (/Invalid|revoked|expired|invalid_grant/i.test(e.message)) {\n    return promptReExportToken(e.message);\n  }\n  throw e; // transient/network — let caller retry\n}","preventionTips":["Export and import the token promptly — refresh tokens from old or logged-out sessions are usually revoked.","Surface the wrapped inner message; it contains the upstream error that names the real cause.","Note that validateImportToken probes via the social refresh path — AWS SSO-sourced tokens may need the OIDC path instead.","Only retry when the embedded message indicates 5xx/throttling, never on auth rejections."],"tags":["oauth","token-import","token-refresh","validation"],"backgroundTag":"oauth-token-refresh-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}