musistudio/claude-code-router · error · Error
Kimi CLI OAuth token refresh returned an incomplete token re
Error message
Kimi CLI OAuth token refresh returned an incomplete token response.
What it means
The Kimi OAuth token refresh succeeded at HTTP level but the JSON response was missing access_token, refresh_token, or expires_in (both snake_case and camelCase variants were checked). This means the auth server returned an unexpected payload shape.
Source
Thrown at packages/core/src/agents/local-providers/kimi.ts:432
"content-type": "application/x-www-form-urlencoded"
},
method: "POST",
signal: controller.signal
});
const text = await response.text();
const payload = parseJsonRecord(text);
if (!response.ok) {
const message = `Kimi CLI OAuth token refresh returned HTTP ${response.status}${tokenRefreshErrorMessage(payload, text)}`;
if (response.status === 401 || response.status === 403) {
throw new KimiRefreshAuthError(response.status, message);
}
throw new Error(message);
}
const accessToken = readString(payload?.access_token) || readString(payload?.accessToken);
const refreshToken = readString(payload?.refresh_token) || readString(payload?.refreshToken);
const expiresIn = numberValue(payload?.expires_in) ?? numberValue(payload?.expiresIn);
if (!accessToken || !refreshToken || !expiresIn) {
throw new Error("Kimi CLI OAuth token refresh returned an incomplete token response.");
}
const refreshed: KimiTokenSet = {
...auth,
accessToken,
expiresAt: Math.floor(Date.now() / 1000) + expiresIn,
expiresIn,
refreshToken,
scope: readString(payload?.scope) || auth.scope || "",
tokenType: readString(payload?.token_type) || readString(payload?.tokenType) || "Bearer"
};
persistKimiAuth(refreshed);
return refreshed;
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
throw new Error(`Kimi CLI OAuth token refresh timed out after ${kimiOauthRefreshTimeoutMs}ms.`);
}
throw error;
} finally {View on GitHub (pinned to 99f24806c6)
Solutions
- Log the raw response text to see what the server actually returned.
- Check for corporate proxies/SSL interception mangling responses.
- Update to the latest version of the package in case the schema handling changed.
- If token shape truly changed, extend readString/numberValue mapping in refreshKimiAuth and re-login.
Defensive patterns
Strategy: try-catch
Type guard
function isIncompleteTokenResponse(e: unknown): boolean {
return e instanceof Error && e.message.includes('incomplete token response');
} Try / catch
try { await refreshKimiAuth(auth); } catch (e) { if (isIncompleteTokenResponse(e)) logAuthPayloadForDiagnosis(); throw e; } Prevention
- Log raw response bodies for OAuth calls.
- Pin to tested auth endpoint versions.
When it happens
Trigger: refreshKimiAuth parses the response with parseJsonRecord and all of access_token/refresh_token/expires_in are absent or non-string/number. Caused by API changes, an HTML error page parsed as JSON, or a proxy intercepting the response.
Common situations: Moonshot changes response schema, an auth proxy returns an error body with 200, or a partial/truncated response body.
Related errors
- Kimi CLI refresh token was not found.
- Kimi CLI OAuth token refresh returned HTTP ${response.status
- Kimi CLI OAuth token refresh timed out after ${kimiOauthRefr
- Grok CLI OAuth token refresh did not return an access token.
- Grok CLI OAuth token refresh timed out after ${timeoutMs}ms.
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/49cd0c1ba880e7b6.
Report an issue: GitHub.