{"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/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/cluster-reconnection-tracker.ts#L101-L131","documentation":"Thrown from ClusterReconnectionTracker.#getDelay (cluster-reconnection-tracker.ts:119) at runtime, when a user-supplied topologyRefreshOnReconnectionAttemptStrategy FUNCTION returns a value that is not false | undefined | 0 and also not a non-negative integer. Unlike the constructor validation (error 28), this fires during a live reconnection attempt, not at construction.","triggerScenarios":"Provide a function strategy that returns a string, negative number, float, true, or an object; a node reconnection occurs and onReconnectionAttempt evaluates the function's return value.","commonSituations":"Strategy functions reading config dynamically and returning a string delay; returning -1 to mean 'skip'; returning null instead of undefined/false/0.","solutions":["Return false, undefined, or 0 to skip the refresh, or a non-negative integer (ms delay) — nothing else.","Coerce/round inside the function: const d = Math.round(value); return Number.isInteger(d) && d >= 0 ? d : false;","Unit-test the strategy against onReconnectionAttempt to catch bad returns before production."],"exampleFix":"// before\nstrategy: (firstReconnectionAt) => `${Date.now() - firstReconnectionAt}` // string -> TypeError at runtime\n\n// after\nstrategy: (firstReconnectionAt) => Math.max(0, Math.round(Date.now() - firstReconnectionAt))","handlingStrategy":"validation","validationCode":"function safeStrategy(firstReconnectionAt: number) {\n  const d = userFn(firstReconnectionAt);\n  if (d === false || d === undefined || d === 0) return false;\n  if (typeof d === 'number' && Number.isInteger(d) && d >= 0) return d;\n  return false; // never let an invalid value escape\n}","typeGuard":"function isValidDelay(d: unknown): boolean {\n  return d === false || d === undefined || (typeof d === 'number' && Number.isInteger(d) && d >= 0);\n}","tryCatchPattern":"cluster.on('error', (e) => {\n  if (/topologyRefreshOnReconnectionAttempt should return/.test(e.message)) {\n    // fix the strategy function's return type\n  }\n});","preventionTips":["Return only false|undefined|0 or a non-negative integer from the strategy.","Round/coerce dynamic config inside the function before returning.","Listen for 'error' to catch runtime returns you missed in testing."],"tags":["cluster","configuration","reconnect","runtime"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}