musistudio/claude-code-router · error · Error

Kimi CLI refresh token was not found.

Error message

Kimi CLI refresh token was not found.

What it means

refreshKimiAuth requires a refresh token to mint a new access token, but the stored KimiTokenSet has an empty/absent refreshToken. Without it the OAuth refresh grant cannot even be attempted, so the error is thrown before any network call.

Source

Thrown at packages/core/src/agents/local-providers/kimi.ts:399

  return {
    key: configured.oauthKey,
    ...(configured.oauthHost ? { oauthHost: configured.oauthHost } : {})
  };
}

function findKimiOauthProvider(reference?: KimiOauthReference): KimiConfiguredProvider | undefined {
  const configured = readKimiConfiguredProviders().filter((item) => Boolean(item.oauthKey && !item.apiKey));
  const key = reference?.key?.trim();
  if (!key) return configured[0];
  const oauthHost = reference?.oauthHost?.trim().replace(/\/+$/, "");
  return configured.find((item) =>
    item.oauthKey === key && (!oauthHost || item.oauthHost?.replace(/\/+$/, "") === oauthHost)
  ) ?? configured.find((item) => item.oauthKey === key);
}

async function refreshKimiAuth(auth: KimiTokenSet): Promise<KimiTokenSet> {
  if (!auth.refreshToken) {
    throw new Error("Kimi CLI refresh token was not found.");
  }
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), kimiOauthRefreshTimeoutMs);
  const oauthHost = (auth.oauthHost || kimiOauthHost).replace(/\/+$/, "");
  try {
    const response = await fetchWithSystemProxy(`${oauthHost}/api/oauth/token`, {
      body: new URLSearchParams({
        client_id: kimiOauthClientId,
        grant_type: "refresh_token",
        refresh_token: auth.refreshToken
      }).toString(),
      headers: {
        ...withoutHeader(kimiIdentityHeaders(), "user-agent"),
        accept: "application/json",
        "content-type": "application/x-www-form-urlencoded"
      },
      method: "POST",
      signal: controller.signal

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Re-run the full kimi OAuth login to obtain a token set that includes a refresh token
  2. Inspect the stored credentials file to confirm refresh_token is present and non-empty
  3. If the provider no longer issues refresh tokens, fall back to API key authentication
  4. Ensure the login flow requests offline access scope
Defensive patterns

Strategy: validation

Validate before calling

const auth = await resolveKimiAuth(ref).catch(() => undefined);
if (auth && kimiAccessTokenExpired(auth) && !auth.refreshToken) {
  await kimiLogin(); // cannot refresh, must re-auth
}

Type guard

function canRefreshKimi(auth: KimiTokenSet): boolean {
  return typeof auth.refreshToken === 'string' && auth.refreshToken.length > 0;
}

Try / catch

catch (e) {
  if (e instanceof Error && e.message === 'Kimi CLI refresh token was not found.') {
    await kimiLogin(); // no retry path exists
  }
}

Prevention

When it happens

Trigger: resolveKimiAuth decides the access token is expired and calls refreshKimiAuth, but the persisted token set was created from a flow that never issued a refresh token, or the field was lost/cleared in config.

Common situations: API-key-based setups that later switched to OAuth with a partial token set; config migration dropped the refresh_token field; the provider issued only short-lived tokens without offline_access scope.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/bb5a50d22ab3cea8. Report an issue: GitHub.