{"record":{"id":"20731fa3b482c00d","repo":"router-for-me/CLIProxyAPI","slug":"kimi-refresh-request-failed-w","errorCode":null,"errorMessage":"kimi: refresh request failed: %w","messagePattern":"kimi: refresh request failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/kimi/kimi.go","lineNumber":387,"sourceCode":"func (c *DeviceFlowClient) refreshTokenSingleFlight(ctx context.Context, refreshToken string) (*KimiTokenData, error) {\n\tdata := url.Values{}\n\tdata.Set(\"client_id\", kimiClientID)\n\tdata.Set(\"grant_type\", \"refresh_token\")\n\tdata.Set(\"refresh_token\", refreshToken)\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, kimiTokenURL, strings.NewReader(data.Encode()))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"kimi: failed to create refresh request: %w\", err)\n\t}\n\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))","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/kimi/kimi.go#L369-L405","documentation":"The HTTP round trip for the token refresh POST to /api/oauth/token failed at the transport level (DNS, connection, TLS). Note the refresh runs under singleflight (kimiRefreshGroup) with context.WithoutCancel, so concurrent refreshes share one attempt; if that attempt has a network error, all waiters see it.","triggerScenarios":"Network outage or DNS failure for auth.kimi.com at the moment a stored token crossed the 5-minute expiry threshold and auto-refresh fired; TLS interception failure; connection refused during auth.kimi.com maintenance.","commonSituations":"Server process running where egress is flaky, transient DNS problems in Kubernetes/containers, firewall rules blocking auth.kimi.com while allowing api.kimi.com.","solutions":["Retry after confirming connectivity: curl https://auth.kimi.com/api/oauth/token (expect 4xx, proving reachability)","Open firewall/egress for auth.kimi.com in addition to api.kimi.com — refresh hits the auth host","Check DNS resolution inside the container/host if failures cluster"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if _, err := net.DialTimeout(\"tcp\", \"auth.kimi.com:443\", 3*time.Second); err != nil {\n    // egress to the auth host is down; skip refresh attempt this cycle\n}","typeGuard":"func isKimiNetErr(err error) bool {\n    var netErr net.Error\n    return errors.As(err, &netErr)\n}","tryCatchPattern":"if err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) {\n        time.Sleep(backoff) // refresh again next cycle; token may still be valid\n    }\n}","preventionTips":["Allow egress to auth.kimi.com, not just api.kimi.com","Schedule refresh retries with backoff instead of tight loops","Rely on singleflight — do not fan out parallel refresh calls"],"tags":["kimi","oauth","refresh-token","network","http"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}