{"record":{"id":"d31ddf1cdf5de621","repo":"musistudio/claude-code-router","slug":"kimi-cli-oauth-token-refresh-returned-http-respo","errorCode":null,"errorMessage":"Kimi CLI OAuth token refresh returned HTTP ${response.status}${tokenRefreshErrorMessage(payload, text)}","messagePattern":"Kimi CLI OAuth token refresh returned HTTP (.+?)(.+?)","errorType":"http","errorClass":"KimiRefreshAuthError","httpStatus":401,"severity":"error","filePath":"packages/core/src/agents/local-providers/kimi.ts","lineNumber":424,"sourceCode":"      body: new URLSearchParams({\n        client_id: kimiOauthClientId,\n        grant_type: \"refresh_token\",\n        refresh_token: auth.refreshToken\n      }).toString(),\n      headers: {\n        ...withoutHeader(kimiIdentityHeaders(), \"user-agent\"),\n        accept: \"application/json\",\n        \"content-type\": \"application/x-www-form-urlencoded\"\n      },\n      method: \"POST\",\n      signal: controller.signal\n    });\n    const text = await response.text();\n    const payload = parseJsonRecord(text);\n    if (!response.ok) {\n      const message = `Kimi CLI OAuth token refresh returned HTTP ${response.status}${tokenRefreshErrorMessage(payload, text)}`;\n      if (response.status === 401 || response.status === 403) {\n        throw new KimiRefreshAuthError(response.status, message);\n      }\n      throw new Error(message);\n    }\n    const accessToken = readString(payload?.access_token) || readString(payload?.accessToken);\n    const refreshToken = readString(payload?.refresh_token) || readString(payload?.refreshToken);\n    const expiresIn = numberValue(payload?.expires_in) ?? numberValue(payload?.expiresIn);\n    if (!accessToken || !refreshToken || !expiresIn) {\n      throw new Error(\"Kimi CLI OAuth token refresh returned an incomplete token response.\");\n    }\n    const refreshed: KimiTokenSet = {\n      ...auth,\n      accessToken,\n      expiresAt: Math.floor(Date.now() / 1000) + expiresIn,\n      expiresIn,\n      refreshToken,\n      scope: readString(payload?.scope) || auth.scope || \"\",\n      tokenType: readString(payload?.token_type) || readString(payload?.tokenType) || \"Bearer\"\n    };","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/agents/local-providers/kimi.ts#L406-L442","documentation":"The Kimi OAuth token refresh POST to ${oauthHost}/api/oauth/token returned a non-2xx status; the message embeds the status and any body-extracted error detail. 401/403 responses are wrapped in KimiRefreshAuthError so callers can force re-authentication rather than retry.","triggerScenarios":"resolveKimiAuth calls refreshKimiAuth with an expired access token; the token endpoint rejects the refresh grant — revoked/rotated refresh token (401/403), invalid client_id, expired grant, rate limiting (429), or server error (5xx).","commonSituations":"Logging in from another machine invalidated the refresh token; oauthHost in the stored config points to the wrong environment (staging vs prod); clock skew; upstream Kimi auth service incident.","solutions":["Re-authenticate via kimi login — a 401/403 refresh almost always means the refresh token is dead","Check the embedded status: 429/5xx suggests retrying with backoff instead of re-login","Verify oauthHost in the stored auth matches the real token endpoint host","Confirm system clock accuracy"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isKimiRefreshAuthError(e: unknown): e is KimiRefreshAuthError {\n  return e instanceof KimiRefreshAuthError;\n}","tryCatchPattern":"try {\n  await resolveKimiAuth(ref);\n} catch (e) {\n  if (e instanceof KimiRefreshAuthError) {\n    await kimiLogin(); // 401/403 → re-auth\n  } else if (e instanceof Error && /returned HTTP 5\\d\\d|429/.test(e.message)) {\n    await retryWithBackoff();\n  } else throw e;\n}","preventionTips":["Single-session logins: avoid concurrent logins that rotate/kill refresh tokens","Store new refresh tokens immediately after rotation","Handle 429 with backoff rather than hammering the token endpoint"],"tags":["kimi","oauth","token-refresh","http-status","authentication"],"backgroundTag":"oauth-refresh-token-invalid","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}