{"record":{"id":"dd53ff2fe4a737f0","repo":"router-for-me/CLIProxyAPI","slug":"kimi-refresh-failed-with-status-d-s","errorCode":null,"errorMessage":"kimi: refresh failed with status %d: %s","messagePattern":"kimi: refresh failed with status (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/kimi/kimi.go","lineNumber":405,"sourceCode":"\t\treturn nil, fmt.Errorf(\"kimi: refresh request failed: %w\", err)\n\t}\n\tdefer func() {\n\t\tif errClose := resp.Body.Close(); errClose != nil {\n\t\t\tlog.Errorf(\"kimi refresh token: close body error: %v\", errClose)\n\t\t}\n\t}()\n\n\tbodyBytes, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"kimi: failed to read refresh response: %w\", err)\n\t}\n\n\tif resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {\n\t\treturn nil, fmt.Errorf(\"kimi: refresh token rejected (status %d)\", resp.StatusCode)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"kimi: refresh failed with status %d: %s\", resp.StatusCode, string(bodyBytes))\n\t}\n\n\tvar tokenResp struct {\n\t\tAccessToken  string  `json:\"access_token\"`\n\t\tRefreshToken string  `json:\"refresh_token\"`\n\t\tTokenType    string  `json:\"token_type\"`\n\t\tExpiresIn    float64 `json:\"expires_in\"`\n\t\tScope        string  `json:\"scope\"`\n\t}\n\n\tif err = json.Unmarshal(bodyBytes, &tokenResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"kimi: failed to parse refresh response: %w\", err)\n\t}\n\n\tif tokenResp.AccessToken == \"\" {\n\t\treturn nil, fmt.Errorf(\"kimi: empty access token in refresh response\")\n\t}\n","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/kimi/kimi.go#L387-L423","documentation":"The refresh endpoint returned a non-200 status other than 401/403, and the full body is embedded in the error. Typical statuses are 429 (rate limiting from too-frequent refreshes) or 5xx (auth.kimi.com errors). The credential may still be valid — this is usually a transient server-side condition.","triggerScenarios":"Many tokens expiring simultaneously causing refresh storms that trip 429; auth.kimi.com 500/502/503 during incidents; oversized/blocked requests through a proxy returning 400/502.","commonSituations":"Fleets of proxy instances refreshing at aligned times, Moonshot auth outages, proxy-induced 502s for POST bodies.","solutions":["Read the embedded status and body: 429 means back off (respect Retry-After, or wait 30-60s) then retry refresh — the token is still good","5xx: retry with backoff; check Moonshot status pages for auth.kimi.com incidents","If a proxy returns 4xx/5xx for the POST, bypass it for auth.kimi.com"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isKimiRefreshStatusErr(err error) (status int, ok bool) {\n    m := regexp.MustCompile(`refresh failed with status (\\d+)`).FindStringSubmatch(err.Error())\n    if len(m) == 2 { s, _ := strconv.Atoi(m[1]); return s, true }\n    return 0, false\n}","tryCatchPattern":"if status, ok := isKimiRefreshStatusErr(err); ok {\n    if status == 429 || status >= 500 {\n        time.Sleep(30 * time.Second) // credential still valid; retry later\n        return c.RefreshToken(ctx, refreshToken)\n    }\n    return err\n}","preventionTips":["Jitter refresh schedules so fleets don't align on 429","Respect Retry-After on 429","Only 401/403 are terminal; everything else is retryable"],"tags":["kimi","oauth","refresh-token","http","rate-limit"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}