redis/node-redis · error · TypeError
topologyRefreshOnReconnectionAttempt should return `false |
Error message
topologyRefreshOnReconnectionAttempt should return `false | undefined | number`, got ${delay} instead What it means
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.
Source
Thrown at packages/client/lib/cluster/cluster-reconnection-tracker.ts:119
*/
#getDelay(firstReconnectionAt: number) {
if (this.#strategy === undefined) {
return ClusterReconnectionTracker.#DEFAULT_TOPOLOGY_REFRESH_ON_RECONNECTION_ATTEMPT;
}
if (this.#strategy === false) {
return;
}
if (typeof this.#strategy === 'number') {
return this.#strategy;
}
const delay = this.#strategy(firstReconnectionAt);
if (delay === false || delay === undefined || delay === 0) return;
if (!Number.isInteger(delay) || delay < 0) {
throw new TypeError(`topologyRefreshOnReconnectionAttempt should return \`false | undefined | number\`, got ${delay} instead`);
}
return delay;
}
#clearTimestampIfClean() {
if (this.#reconnectingClients.size === 0) {
this.#firstReconnectionAt = undefined;
}
}
}
View on GitHub (pinned to bb5beb5657)
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.
Example fix
// before
strategy: (firstReconnectionAt) => `${Date.now() - firstReconnectionAt}` // string -> TypeError at runtime
// after
strategy: (firstReconnectionAt) => Math.max(0, Math.round(Date.now() - firstReconnectionAt)) Defensive patterns
Strategy: validation
Validate before calling
function safeStrategy(firstReconnectionAt: number) {
const d = userFn(firstReconnectionAt);
if (d === false || d === undefined || d === 0) return false;
if (typeof d === 'number' && Number.isInteger(d) && d >= 0) return d;
return false; // never let an invalid value escape
} Type guard
function isValidDelay(d: unknown): boolean {
return d === false || d === undefined || (typeof d === 'number' && Number.isInteger(d) && d >= 0);
} Try / catch
cluster.on('error', (e) => {
if (/topologyRefreshOnReconnectionAttempt should return/.test(e.message)) {
// fix the strategy function's return type
}
}); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Strategy functions reading config dynamically and returning a string delay; returning -1 to mean 'skip'; returning null instead of undefined/false/0.
Related errors
- topologyRefreshOnReconnectionAttempt must be undefined, fals
- Reconnect strategy should return `false | Error | number`, g
- Client Side Caching is only supported with RESP3
- Client reconnected after WATCH
- Cluster already open
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/01bc402333d2f87e.json.
Report an issue: GitHub.