{"record":{"id":"7199dbd01cffff52","repo":"redis/node-redis","slug":"expirationrefreshratio-must-be-greater-or-equal-to","errorCode":null,"errorMessage":"expirationRefreshRatio must be greater or equal to 0","messagePattern":"expirationRefreshRatio must be greater or equal to 0","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/authx/token-manager.ts","lineNumber":149,"sourceCode":" *\n * The TokenManager should be disposed when it is no longer needed by calling the dispose method on the Disposable\n * returned by start.\n */\nexport class TokenManager<T> {\n  private currentToken: Token<T> | null = null;\n  private refreshTimeout: NodeJS.Timeout | null = null;\n  private listener: TokenStreamListener<T> | null = null;\n  private retryAttempt: number = 0;\n\n  constructor(\n    private readonly identityProvider: IdentityProvider<T>,\n    private readonly config: TokenManagerConfig\n  ) {\n    if (this.config.expirationRefreshRatio > 1) {\n      throw new Error('expirationRefreshRatio must be less than or equal to 1');\n    }\n    if (this.config.expirationRefreshRatio < 0) {\n      throw new Error('expirationRefreshRatio must be greater or equal to 0');\n    }\n  }\n\n  /**\n   * Starts the token manager and returns a Disposable that can be used to stop the token manager.\n   *\n   * @param listener The listener that will receive token updates.\n   * @param initialDelayMs The initial delay in milliseconds before the first token refresh.\n   */\n  public start(listener: TokenStreamListener<T>, initialDelayMs: number = 0): Disposable {\n    if (this.listener) {\n      this.stop();\n    }\n\n    this.listener = listener;\n    this.retryAttempt = 0;\n\n    this.scheduleNextRefresh(initialDelayMs);","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/authx/token-manager.ts#L131-L167","documentation":"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.","triggerScenarios":"Constructing TokenManager with expirationRefreshRatio set to a negative number, e.g. -0.1.","commonSituations":"Sign error when computing the ratio from another value; underflow of a computed expression; copy-paste mistake.","solutions":["Clamp the value at a sensible lower bound (e.g. 0.5 to refresh in the first half of TTL).","Fix the upstream calculation that produced a negative number.","Validate at config load time."],"exampleFix":"// before\nexpirationRefreshRatio: ttl < 0 ? ttl / expected : 0.8\n\n// after\nexpirationRefreshRatio: 0.8","handlingStrategy":"validation","validationCode":"function validateTokenManagerConfig(cfg) {\n  const r = cfg.expirationRefreshRatio;\n  if (typeof r !== 'number' || Number.isNaN(r) || r < 0 || r > 1) {\n    throw new Error(`expirationRefreshRatio must be in [0,1], got ${r}`);\n  }\n  return cfg;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate the ratio is a finite number in [0,1] before constructing TokenManager.","Watch for arithmetic that can underflow (e.g. ttl / something where something is huge).","Default to a safe value like 0.8 when in doubt."],"tags":["config","auth","validation","token-manager"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}