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

  1. Inspect the Kimi entry under auths/ — the refresh token field is empty; re-login to regenerate the full credential
  2. Delete the broken auth file and run the Kimi login flow again to write a fresh one
  3. 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

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


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