router-for-me/CLIProxyAPI · error

kimi: refresh failed with status %d: %s

Error message

kimi: refresh failed with status %d: %s

What it means

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.

Source

Thrown at internal/auth/kimi/kimi.go:405

		return nil, fmt.Errorf("kimi: refresh request failed: %w", err)
	}
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			log.Errorf("kimi refresh token: close body error: %v", errClose)
		}
	}()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("kimi: failed to read refresh response: %w", err)
	}

	if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
		return nil, fmt.Errorf("kimi: refresh token rejected (status %d)", resp.StatusCode)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("kimi: refresh failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var tokenResp struct {
		AccessToken  string  `json:"access_token"`
		RefreshToken string  `json:"refresh_token"`
		TokenType    string  `json:"token_type"`
		ExpiresIn    float64 `json:"expires_in"`
		Scope        string  `json:"scope"`
	}

	if err = json.Unmarshal(bodyBytes, &tokenResp); err != nil {
		return nil, fmt.Errorf("kimi: failed to parse refresh response: %w", err)
	}

	if tokenResp.AccessToken == "" {
		return nil, fmt.Errorf("kimi: empty access token in refresh response")
	}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. 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
  2. 5xx: retry with backoff; check Moonshot status pages for auth.kimi.com incidents
  3. If a proxy returns 4xx/5xx for the POST, bypass it for auth.kimi.com
Defensive patterns

Strategy: retry

Type guard

func isKimiRefreshStatusErr(err error) (status int, ok bool) {
    m := regexp.MustCompile(`refresh failed with status (\d+)`).FindStringSubmatch(err.Error())
    if len(m) == 2 { s, _ := strconv.Atoi(m[1]); return s, true }
    return 0, false
}

Try / catch

if status, ok := isKimiRefreshStatusErr(err); ok {
    if status == 429 || status >= 500 {
        time.Sleep(30 * time.Second) // credential still valid; retry later
        return c.RefreshToken(ctx, refreshToken)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: Fleets of proxy instances refreshing at aligned times, Moonshot auth outages, proxy-induced 502s for POST bodies.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/dd53ff2fe4a737f0. Report an issue: GitHub.