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
Companion guard to error [3]: the TokenManager constructor rejects `expirationRefreshRatio < 0`. A negative ratio would compute a negative refresh delay, breaking the scheduling math (Math.floor of a negative ttlMs fraction) and effectively never refreshing correctly. Both bounds checks fire in the constructor, so the TokenManager never enters an inconsistent scheduling state.
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 bb5beb5657)
Solutions
- Set expirationRefreshRatio to a value in [0,1] (e.g. 0.7).
- Validate config at load time and reject non-finite or out-of-range values before constructing the TokenManager.
- If using a sentinel default, coerce it to a valid ratio (e.g. 0.7) when unset.
Example fix
// before
new TokenManager(provider, { expirationRefreshRatio: -1 });
// after
new TokenManager(provider, { expirationRefreshRatio: 0.7 }); Defensive patterns
Strategy: validation
Validate before calling
if (!Number.isFinite(cfg.expirationRefreshRatio) || cfg.expirationRefreshRatio < 0) {
throw new RangeError('expirationRefreshRatio must be a finite number >= 0');
} Type guard
function isNonNegativeRatio(v: unknown): v is number {
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
} Prevention
- Reject sentinel defaults like -1 at config load; coerce to a valid ratio.
- Add a schema check for any config sourced from env/remote.
When it happens
Trigger: Passing `expirationRefreshRatio: -1` or any negative number; an arithmetic slip (subtracting instead of adding, or 0 - value) producing a negative config; reading an unset numeric env var parsed as NaN (note: NaN comparisons are false, so NaN slips past, but a parsed negative literal triggers this).
Common situations: Default/uninitialized config expressed as -1 'sentinel'; sign error in config transformation; env var typo producing a leading minus.
Related errors
- expirationRefreshRatio must be less than or equal to 1
- TokenManager is not running, but refresh was called
- TokenManager is not running, but a new token was received
- TokenManager is not running but received an error: ${errorMe
- tls socket option is set to ${options.socket.tls} which is m
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/7199dbd01cffff52.json.
Report an issue: GitHub.