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
- Use a value in [0,1] (typical 0.7..0.9 to refresh before expiry with margin).
- If you meant 80 percent, write 0.8.
- 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
- Remember expirationRefreshRatio is a ratio in [0,1], not a percentage.
- Centralise token config validation at app startup.
- Document the unit next to the config field in your own settings file.
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
- expirationRefreshRatio must be greater or equal to 0
- TokenManager is not running, but refresh was called
- Client Side Caching is only supported with RESP3
- emitInvalidate is not supported (or necessary) when…
- emitInvalidate is only supported with RESP3
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)