redis/node-redis · error · Error

expirationRefreshRatio must be less than or equal to 1

Error message

expirationRefreshRatio must be less than or equal to 1

What it means

TokenManagerConfig.expirationRefreshRatio controls when a token is refreshed, expressed as a fraction of the token's TTL in [0,1]. A value greater than 1 would schedule a refresh after the token has already expired, which is nonsensical and is rejected in the TokenManager constructor.

Solutions

  1. Use a value in [0,1] (typical 0.7..0.9 to refresh before expiry with margin).
  2. If you meant 80 percent, write 0.8.
  3. Validate the ratio at config load time so the failure is localized.

Example fix

// before
new TokenManager(idp, { expirationRefreshRatio: 80, retryPolicy });

// after
new TokenManager(idp, { expirationRefreshRatio: 0.8, retryPolicy });
Defensive patterns

Strategy: validation

Validate before calling

function validateTokenManagerConfig(cfg) {
  const r = cfg.expirationRefreshRatio;
  if (typeof r !== 'number' || Number.isNaN(r) || r > 1) {
    throw new Error(`expirationRefreshRatio must be in [0,1], got ${r}`);
  }
  return cfg;
}

Prevention

When it happens

Trigger: Constructing new TokenManager(identityProvider, { expirationRefreshRatio: 1.2, retryPolicy: {...} }) with a value above 1.

Common situations: Confusing the ratio (0..1) with a percentage (0..100); copying config from a percent-based library; typo or arithmetic slip computing the value.

Related errors


AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11). Data as JSON: /api/errors/95e8345b27b44230. Report an issue: GitHub.

Appendix: source

Thrown at packages/client/lib/authx/token-manager.ts:146

 * TokenManager is responsible for obtaining/refreshing tokens and notifying listeners about token changes.
 * It uses an IdentityProvider to request tokens. The token refresh is scheduled based on the token's TTL and
 * the expirationRefreshRatio configuration.
 *
 * The TokenManager should be disposed when it is no longer needed by calling the dispose method on the Disposable
 * returned by start.
 */
export class TokenManager<T> {
  private currentToken: Token<T> | null = null;
  private refreshTimeout: NodeJS.Timeout | null = null;
  private listener: TokenStreamListener<T> | null = null;
  private retryAttempt: number = 0;

  constructor(
    private readonly identityProvider: IdentityProvider<T>,
    private readonly config: TokenManagerConfig
  ) {
    if (this.config.expirationRefreshRatio > 1) {
      throw new Error('expirationRefreshRatio must be less than or equal to 1');
    }
    if (this.config.expirationRefreshRatio < 0) {
      throw new Error('expirationRefreshRatio must be greater or equal to 0');
    }
  }

  /**
   * Starts the token manager and returns a Disposable that can be used to stop the token manager.
   *
   * @param listener The listener that will receive token updates.
   * @param initialDelayMs The initial delay in milliseconds before the first token refresh.
   */
  public start(listener: TokenStreamListener<T>, initialDelayMs: number = 0): Disposable {
    if (this.listener) {
      this.stop();
    }

    this.listener = listener;

View on GitHub (pinned to 90fd0652bc)