{"record":{"id":"945c234ee21db145","repo":"router-for-me/CLIProxyAPI","slug":"kimi-failed-to-read-refresh-response-w","errorCode":null,"errorMessage":"kimi: failed to read refresh response: %w","messagePattern":"kimi: failed to read refresh response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/kimi/kimi.go","lineNumber":397,"sourceCode":"\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Accept\", \"application/json\")\n\tfor k, v := range c.commonHeaders() {\n\t\treq.Header.Set(k, v)\n\t}\n\n\tresp, err := c.httpClient.Do(req)\n\tif err != nil {\n\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","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/kimi/kimi.go#L379-L415","documentation":"Reading the refresh response body failed mid-stream after response headers arrived (io.ReadAll error on POST /api/oauth/token with grant_type=refresh_token). Transport-level interruption, not an OAuth decision — the status line was received but the payload was cut off.","triggerScenarios":"Connection reset or proxy stream cut during refresh response; truncated chunked transfer; idle-connection reuse race where the pooled connection is closed just as the body streams.","commonSituations":"Keep-alive connection reuse races under load (server closes pooled connection mid-read), proxies with aggressive stream timeouts, flaky egress.","solutions":["Retry the refresh — connection-reuse races clear on a new connection; singleflight dedup means just invoking RefreshToken again suffices","If frequent, reduce httpClient idle-connection reuse issues by adjusting transport settings or restarting the process","Check for intermediary proxies truncating long-lived connections to auth.kimi.com"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"failed to read refresh response\") {\n    // connection reuse race: immediate retry on fresh connection usually succeeds\n    return c.RefreshToken(ctx, refreshToken)\n}","preventionTips":["Retry refresh once on mid-body read failures","Tune transport idle-connection settings if races recur","Avoid proxies that cut long response streams"],"tags":["kimi","oauth","refresh-token","network","http"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}