redis/node-redis · error · Error

expirationRefreshRatio must be greater or equal to 0

Error message

expirationRefreshRatio must be greater or equal to 0

What it means

The companion guard to error 4: expirationRefreshRatio must also be >= 0. A negative ratio has no meaning (refresh before token issuance) and is rejected by the TokenManager constructor.

Solutions

  1. Clamp the value at a sensible lower bound (e.g. 0.5 to refresh in the first half of TTL).
  2. Fix the upstream calculation that produced a negative number.
  3. Validate at config load time.

Example fix

// before
expirationRefreshRatio: ttl < 0 ? ttl / expected : 0.8

// after
expirationRefreshRatio: 0.8
Defensive patterns

Strategy: validation

Validate before calling

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

Prevention

When it happens

Trigger: Constructing TokenManager with expirationRefreshRatio set to a negative number, e.g. -0.1.

Common situations: Sign error when computing the ratio from another value; underflow of a computed expression; copy-paste mistake.

Related errors


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

Appendix: source

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

 *
 * 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;
    this.retryAttempt = 0;

    this.scheduleNextRefresh(initialDelayMs);

View on GitHub (pinned to 90fd0652bc)