{"record":{"id":"85209c8ebc65d51d","repo":"router-for-me/CLIProxyAPI","slug":"kimi-token-request-failed-w","errorCode":null,"errorMessage":"kimi: token request failed: %w","messagePattern":"kimi: token request failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/kimi/kimi.go","lineNumber":285,"sourceCode":"func (c *DeviceFlowClient) exchangeDeviceCode(ctx context.Context, deviceCode string) (*KimiTokenData, error, bool) {\n\tdata := url.Values{}\n\tdata.Set(\"client_id\", kimiClientID)\n\tdata.Set(\"device_code\", deviceCode)\n\tdata.Set(\"grant_type\", \"urn:ietf:params:oauth:grant-type:device_code\")\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 token request: %w\", err), false\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: token request failed: %w\", err), false\n\t}\n\tdefer func() {\n\t\tif errClose := resp.Body.Close(); errClose != nil {\n\t\t\tlog.Errorf(\"kimi token exchange: 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 token response: %w\", err), false\n\t}\n\n\t// Parse response - Kimi returns 200 for both success and pending states\n\tvar oauthResp struct {\n\t\tError            string  `json:\"error\"`\n\t\tErrorDescription string  `json:\"error_description\"`\n\t\tAccessToken      string  `json:\"access_token\"`\n\t\tRefreshToken     string  `json:\"refresh_token\"`","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/kimi/kimi.go#L267-L303","documentation":"The HTTP round trip for the device-code-to-token exchange (POST /api/oauth/token) failed at the transport level. This is the per-tick request inside PollForToken; it returns shouldContinue=false, so polling aborts rather than retrying the tick. Causes include DNS failure, connection refused, TLS errors, or the polling context being cancelled mid-request.","triggerScenarios":"DNS resolution failure for auth.kimi.com during the poll, network dropped while waiting for the user to authorize, TLS handshake failure, ctx cancelled so the in-flight request is aborted (surfaces here as a Do error).","commonSituations":"Laptop sleep/resume during the authorization wait, Wi-Fi switch, container network restart, aggressive DNS timeouts in minimal container images.","solutions":["Verify connectivity to auth.kimi.com (curl https://auth.kimi.com) and retry the login — transient network loss aborts the whole poll","If the machine sleeps during login, keep it awake or re-run login after reconnecting","Note that one failed tick ends the flow by design; do not rely on it self-healing — restart StartDeviceFlow + PollForToken"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Keep the network alive during the multi-minute poll\n// (e.g. disable sleep, or run poll in a process that stays connected)","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        // one transport hiccup ends the poll by design: restart the flow\n        return restartLogin()\n    }\n    return err\n}","preventionTips":["Prevent machine sleep during interactive login","Validate egress to auth.kimi.com before starting the flow","Wrap the whole login in a retry-once wrapper since a failed tick is terminal"],"tags":["kimi","oauth","device-flow","network","http"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}