{"record":{"id":"01bc402333d2f87e","repo":"redis/node-redis","slug":"topologyrefreshonreconnectionattempt-should-return","errorCode":null,"errorMessage":"topologyRefreshOnReconnectionAttempt should return `false | undefined | number`, got ${delay} instead","messagePattern":"topologyRefreshOnReconnectionAttempt should return `false \\| undefined \\| number`, got (.+?) instead","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/cluster-reconnection-tracker.ts","lineNumber":119,"sourceCode":"   */\n  #getDelay(firstReconnectionAt: number) {\n    if (this.#strategy === undefined) {\n      return ClusterReconnectionTracker.#DEFAULT_TOPOLOGY_REFRESH_ON_RECONNECTION_ATTEMPT;\n    }\n\n    if (this.#strategy === false) {\n      return;\n    }\n\n    if (typeof this.#strategy === 'number') {\n      return this.#strategy;\n    }\n\n    const delay = this.#strategy(firstReconnectionAt);\n    if (delay === false || delay === undefined || delay === 0) return;\n\n    if (!Number.isInteger(delay) || delay < 0) {\n      throw new TypeError(`topologyRefreshOnReconnectionAttempt should return \\`false | undefined | number\\`, got ${delay} instead`);\n    }\n\n    return delay;\n  }\n\n  #clearTimestampIfClean() {\n    if (this.#reconnectingClients.size === 0) {\n      this.#firstReconnectionAt = undefined;\n    }\n  }\n}\n","sourceCodeStart":101,"sourceCodeEnd":131,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/cluster/cluster-reconnection-tracker.ts#L101-L131","documentation":"Thrown inside ClusterReconnectionTracker.#getDelay when a user-supplied topologyRefreshOnReconnectionAttemptStrategy function returns a value that is not false, undefined, 0, or a non-negative integer. Unlike the constructor-time validation, this fires at runtime during a live reconnection cycle when the function is actually called.","triggerScenarios":"Providing topologyRefreshOnReconnectionAttemptStrategy as a function that returns a negative number, a float, a string, null, or a Promise. Triggered when a cluster node starts reconnecting and the tracker evaluates the delay.","commonSituations":"A custom strategy function with an off-by-one or Math.round omission returning 5.5; returning a string delay; returning null instead of undefined/false; logic bug that returns NaN when Date.now() math goes wrong.","solutions":["Ensure the strategy function returns false, undefined, 0, or a non-negative integer only.","Round/clamp the computed delay: return Math.max(0, Math.round(delay)).","Add a unit test for the strategy function covering edge inputs (0, negative, NaN).","If you do not need custom logic, use a plain integer or undefined instead of a function."],"exampleFix":"// before\ntopologyRefreshOnReconnectionAttemptStrategy: (firstAt) => (Date.now() - firstAt) / 2 // float -> TypeError\n\n// after\ntopologyRefreshOnReconnectionAttemptStrategy: (firstAt) => Math.max(0, Math.round((Date.now() - firstAt) / 2))","handlingStrategy":"validation","validationCode":"function safeStrategy(firstReconnectionAt: number): false | undefined | number {\n  const delay = Math.max(0, Math.round(computeDelay(firstReconnectionAt)));\n  return Number.isFinite(delay) ? delay : false;\n}","typeGuard":"function isValidStrategyReturn(v: unknown): v is false | undefined | number {\n  return v === false || v === undefined || (typeof v === 'number' && Number.isInteger(v) && v >= 0);\n}","tryCatchPattern":null,"preventionTips":["Round and clamp strategy return values: Math.max(0, Math.round(x)).","Guard against NaN/Infinity in Date.now() arithmetic.","Unit-test the strategy function across edge inputs."],"tags":["cluster","configuration","validation","reconnect"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}