router-for-me/CLIProxyAPI · error
kimi: token request failed: %w
Error message
kimi: token request failed: %w
What it means
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.
Source
Thrown at internal/auth/kimi/kimi.go:285
func (c *DeviceFlowClient) exchangeDeviceCode(ctx context.Context, deviceCode string) (*KimiTokenData, error, bool) {
data := url.Values{}
data.Set("client_id", kimiClientID)
data.Set("device_code", deviceCode)
data.Set("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
req, err := http.NewRequestWithContext(ctx, http.MethodPost, kimiTokenURL, strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("kimi: failed to create token request: %w", err), false
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
for k, v := range c.commonHeaders() {
req.Header.Set(k, v)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("kimi: token request failed: %w", err), false
}
defer func() {
if errClose := resp.Body.Close(); errClose != nil {
log.Errorf("kimi token exchange: close body error: %v", errClose)
}
}()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("kimi: failed to read token response: %w", err), false
}
// Parse response - Kimi returns 200 for both success and pending states
var oauthResp struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`View on GitHub (pinned to 78f0c4079e)
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
Defensive patterns
Strategy: retry
Validate before calling
// Keep the network alive during the multi-minute poll // (e.g. disable sleep, or run poll in a process that stays connected)
Type guard
func isKimiNetErr(err error) bool {
var netErr net.Error
return errors.As(err, &netErr)
} Try / catch
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) {
// one transport hiccup ends the poll by design: restart the flow
return restartLogin()
}
return err
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: Laptop sleep/resume during the authorization wait, Wi-Fi switch, container network restart, aggressive DNS timeouts in minimal container images.
Related errors
- kimi: failed to read device code response: %w
- kimi: failed to read token response: %w
- kimi: device code request failed: %w
- kimi: device code request failed with status %d: %s
- kimi: failed to create token request: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/85209c8ebc65d51d.
Report an issue: GitHub.