{"record":{"id":"49cd0c1ba880e7b6","repo":"musistudio/claude-code-router","slug":"kimi-cli-oauth-token-refresh-returned-an-incomplet","errorCode":null,"errorMessage":"Kimi CLI OAuth token refresh returned an incomplete token response.","messagePattern":"Kimi CLI OAuth token refresh returned an incomplete token response\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/agents/local-providers/kimi.ts","lineNumber":432,"sourceCode":"        \"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    };\n    persistKimiAuth(refreshed);\n    return refreshed;\n  } catch (error) {\n    if (error instanceof Error && error.name === \"AbortError\") {\n      throw new Error(`Kimi CLI OAuth token refresh timed out after ${kimiOauthRefreshTimeoutMs}ms.`);\n    }\n    throw error;\n  } finally {","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/agents/local-providers/kimi.ts#L414-L450","documentation":"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.","triggerScenarios":"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.","commonSituations":"Moonshot changes response schema, an auth proxy returns an error body with 200, or a partial/truncated response body.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isIncompleteTokenResponse(e: unknown): boolean {\n  return e instanceof Error && e.message.includes('incomplete token response');\n}","tryCatchPattern":"try { await refreshKimiAuth(auth); } catch (e) { if (isIncompleteTokenResponse(e)) logAuthPayloadForDiagnosis(); throw e; }","preventionTips":["Log raw response bodies for OAuth calls.","Pin to tested auth endpoint versions."],"tags":["kimi","oauth","token-response","schema"],"backgroundTag":"oauth-token-response-malformed","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}