redis/node-redis · error · TypeError

Reconnect strategy should return `false | Error | number`…

Error message

Reconnect strategy should return `false | Error | number`, got ${retryIn} instead

What it means

Thrown inside the wrapped reconnectStrategy when the user-supplied function returns a value that is not false, an Error, or a number. The library validates the return type on every reconnect decision because an invalid delay would break the backoff loop. The thrown TypeError is caught internally, emitted as an 'error' event, and the default strategy is used as a fallback.

Solutions

  1. Make the custom reconnectStrategy return a number (ms delay), false (stop reconnecting), or an Error (stop with error) — nothing else.
  2. If using a string from config, coerce with Number() and validate before returning.
  3. Ensure the function is synchronous; do not return a Promise.
  4. Listen for the 'error' event to confirm whether the fallback default strategy took over.

Example fix

// before
reconnectStrategy(retries) {
  return retries < 10 ? '500' : false; // string -> TypeError
}

// after
reconnectStrategy(retries) {
  return retries < 10 ? 500 : false; // number | false
}
Defensive patterns

Strategy: validation

Validate before calling

function validStrategy(retries: number, cause: Error): false | Error | number {
  const delay = Math.min(retries * 50, 2000);
  return delay;
}
// test: assert typeof validStrategy(0,new Error()) === 'number' || v === false || v instanceof Error

Type guard

function isValidReconnectReturn(v: unknown): v is false | Error | number {
  return v === false || v instanceof Error || typeof v === 'number';
}

Prevention

When it happens

Trigger: Passing socket: { reconnectStrategy(retries, cause) { return '500'; } } (string), or returning null, undefined, a Promise, or NaN from the custom strategy function. Any non-(false|Error|number) return triggers it.

Common situations: Typo in a custom strategy returning a string like '500' instead of 500; returning retryIn > someMax as a boolean expression that evaluates to undefined; migrating from an older client whose strategy contract differed; returning a Promise instead of a plain value.

Related errors


AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11). Data as JSON: /api/errors/e653e1fa01f529b0. Report an issue: GitHub.

Appendix: source

Thrown at packages/client/lib/client/socket.ts:125

    this.#connectTimeout = options?.connectTimeout ?? 5000;
    this.#reconnectStrategy = this.#createReconnectStrategy(options);
    this.#socketFactory = this.#createSocketFactory(options);
    this.#socketTimeout = options?.socketTimeout;
    this.#clientId = clientId;
  }

  #createReconnectStrategy(options?: RedisSocketOptions): ReconnectStrategyFunction {
    const strategy = options?.reconnectStrategy;
    if (strategy === false || typeof strategy === 'number') {
      return () => strategy;
    }

    if (strategy) {
      return (retries, cause) => {
        try {
const retryIn = strategy(retries, cause);
          if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') {
            throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`);
          }
          return retryIn;
        } catch (err) {
          publish(CHANNELS.ERROR, () => ({
            error: err as Error,
            origin: 'client',
            internal: false,
            clientId: this.#clientId
          }));
          this.emit('error', err);
          return this.defaultReconnectStrategy(retries, err);
        }
      };
    }

    return this.defaultReconnectStrategy;
  }

View on GitHub (pinned to 90fd0652bc)