{"record":{"id":"6772e1a564310c17","repo":"decolua/9router","slug":"refresh-token-is-required-6772e1","errorCode":null,"errorMessage":"refresh token is required","messagePattern":"refresh token is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/kiroExternalIdp.js","lineNumber":135,"sourceCode":"    email,\n    providerSpecificData: {\n      profileArn,\n      region,\n      authMethod: \"external_idp\",\n      provider: \"CLIProxyAPI\",\n      clientId,\n      tokenEndpoint,\n      scope,\n    },\n  };\n}\n\nexport function buildExternalIdpRefreshParams(refreshToken, providerSpecificData = {}) {\n  const clientId = normalizeString(providerSpecificData.clientId || providerSpecificData.client_id);\n  const tokenEndpoint = validateMicrosoftTokenEndpoint(providerSpecificData.tokenEndpoint || providerSpecificData.token_endpoint);\n  const scope = normalizeScope(providerSpecificData.scope || providerSpecificData.scopes);\n\n  if (!refreshToken) throw new Error(\"refresh token is required\");\n  if (!clientId) throw new Error(\"clientId is required for external_idp refresh\");\n  if (!scope) throw new Error(\"scope is required for external_idp refresh\");\n\n  return {\n    tokenEndpoint,\n    body: new URLSearchParams({\n      grant_type: \"refresh_token\",\n      client_id: clientId,\n      refresh_token: refreshToken,\n      scope,\n    }),\n    providerSpecificData: {\n      ...providerSpecificData,\n      authMethod: \"external_idp\",\n      clientId,\n      tokenEndpoint,\n      scope,\n    },","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/kiroExternalIdp.js#L117-L153","documentation":"buildExternalIdpRefreshParams builds the x-www-form-urlencoded body for refreshing a Kiro external-IdP token via the Microsoft endpoint. The refreshToken argument must be a non-empty string; this guard fires when it is null, undefined, empty, or whitespace. Note this is a distinct message ('refresh token is required') from the importer's 'refresh_token is required'.","triggerScenarios":"Calling buildExternalIdpRefreshParams(refreshToken, data) with refreshToken undefined/null/\"\" — typically because the stored account has no refresh token, or the caller read the wrong field from providerSpecificData.","commonSituations":"A Kiro account imported without a refresh token now reaching its first refresh; refreshToken stored under a different key so the caller passes undefined; a partially completed import that validated access fields but skipped refresh-token checks upstream.","solutions":["Ensure the account was imported through normalizeKiroExternalIdpAuth, which enforces refresh_token presence, so providerSpecificData carries it","At the call site, read the refresh token from the correct credential field and pass it as the first argument","If the refresh token was lost/expired, re-run the external IdP login to obtain a new one and re-import","Guard the call: skip refresh and force re-auth when refreshToken is falsy instead of calling with an empty value"],"exampleFix":"// before\nconst params = buildExternalIdpRefreshParams(account.refreshToken, account.providerSpecificData);\n// after\nif (!account.refreshToken) {\n  throw new Error('Kiro account has no refresh token; re-authentication required');\n}\nconst params = buildExternalIdpRefreshParams(account.refreshToken, account.providerSpecificData);","handlingStrategy":"try-catch","validationCode":"if (typeof refreshToken !== 'string' || !refreshToken.trim()) {\n  // force re-authentication path instead of attempting refresh\n  return scheduleReauth(accountId);\n}","typeGuard":"function hasRefreshToken(v) {\n  return typeof v === 'string' && v.trim() !== '';\n}","tryCatchPattern":"try {\n  const params = buildExternalIdpRefreshParams(account.refreshToken, account.providerSpecificData);\n} catch (e) {\n  if (e.message === 'refresh token is required') {\n    // credential unusable: mark account and require interactive re-login\n    markAccountNeedsReauth(account.id);\n    return;\n  }\n  throw e;\n}","preventionTips":["Only create accounts via normalizeKiroExternalIdpAuth, which guarantees a refresh token exists","Check refreshToken presence when loading accounts and route empty ones to re-auth early","Persist the refresh token under a stable key so reads never come back undefined","Treat a lost refresh token as unrecoverable — refresh tokens cannot be regenerated without re-login"],"tags":["oauth","refresh-token","missing-argument","token-refresh"],"backgroundTag":"missing-refresh-token","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}