router-for-me/CLIProxyAPI · error
kimi: refresh token is required
Error message
kimi: refresh token is required
What it means
RefreshToken was called with an empty or whitespace-only refresh token string. This is pure input validation before any network call — the stored credential is missing/blank, indicating corrupted or incomplete auth state rather than a server rejection.
Source
Thrown at internal/auth/kimi/kimi.go:349
var expiresAt int64
if oauthResp.ExpiresIn > 0 {
expiresAt = time.Now().Unix() + int64(oauthResp.ExpiresIn)
}
return &KimiTokenData{
AccessToken: oauthResp.AccessToken,
RefreshToken: oauthResp.RefreshToken,
TokenType: oauthResp.TokenType,
ExpiresAt: expiresAt,
Scope: oauthResp.Scope,
}, nil, false
}
// RefreshToken exchanges a refresh token for a new access token.
func (c *DeviceFlowClient) RefreshToken(ctx context.Context, refreshToken string) (*KimiTokenData, error) {
if strings.TrimSpace(refreshToken) == "" {
return nil, fmt.Errorf("kimi: refresh token is required")
}
if ctx == nil {
ctx = context.Background()
}
refreshToken = strings.TrimSpace(refreshToken)
result, err, _ := kimiRefreshGroup.Do(refreshToken, func() (interface{}, error) {
return c.refreshTokenSingleFlight(context.WithoutCancel(ctx), refreshToken)
})
if err != nil {
return nil, err
}
tokenData, ok := result.(*KimiTokenData)
if !ok || tokenData == nil {
return nil, fmt.Errorf("kimi: refresh token failed: invalid single-flight result")
}
return tokenData, nil
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Inspect the Kimi entry under auths/ — the refresh token field is empty; re-login to regenerate the full credential
- Delete the broken auth file and run the Kimi login flow again to write a fresh one
- If it recurs, check for crashes or concurrent writes corrupting auth storage
Example fix
// before
refreshToken := strings.TrimSpace(tokenStorage.RefreshToken)
newTok, err := c.deviceClient.RefreshToken(ctx, refreshToken) // crashes into validation error when empty
// after
refreshToken := strings.TrimSpace(tokenStorage.RefreshToken)
if refreshToken == "" {
return fmt.Errorf("kimi: no refresh token stored; re-login required")
}
newTok, err := c.deviceClient.RefreshToken(ctx, refreshToken) Defensive patterns
Strategy: validation
Validate before calling
refreshToken := strings.TrimSpace(tok.RefreshToken)
if refreshToken == "" {
return fmt.Errorf("kimi: stored refresh token missing; re-login required")
} Type guard
func hasKimiRefreshToken(t *KimiTokenStorage) bool {
return t != nil && strings.TrimSpace(t.RefreshToken) != ""
} Try / catch
if err != nil && strings.Contains(err.Error(), "refresh token is required") {
// trigger interactive re-login; do not retry with the same blank value
} Prevention
- Validate stored credentials at load time
- Write auth files atomically to avoid truncated JSON
- Never hand-edit auths/*.json
When it happens
Trigger: Token storage under auths/ was hand-edited or partially written so refresh_token is empty; code path loads a KimiTokenStorage JSON that predates refresh tokens; caller passes the wrong variable.
Common situations: Truncated JSON in the auth file after a crash during save, migration from an older storage format, manual tampering with auths/*.json.
Related errors
- kimi: failed to create refresh request: %w
- kimi: refresh request failed: %w
- kimi: failed to read refresh response: %w
- kimi: refresh token rejected (status %d)
- kimi: refresh failed with status %d: %s
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/2159aefb6753274c.
Report an issue: GitHub.