redis/node-redis · error · Error

pubSubProxy: didn't define node to do pubsub against

Error message

pubSubProxy: didn't define node to do pubsub against

What it means

`PubSubProxy` is told which node to pubsub against via `changeNode(node)`; `#createClient` throws if `#node` is still undefined when it tries to build a `RedisClient`. It is an internal ordering invariant: pubsub commands must not run before the Sentinel has selected a target node.

Source

Thrown at packages/client/lib/sentinel/pub-sub-proxy.ts:48

  #onError;

  #node?: RedisNode;
  #state?: PubSubState;
  #subscriptions?: Subscriptions;

  constructor(
    clientOptions: AnyRedisClientOptions,
    onError: OnError
  ) {
    super();

    this.#clientOptions = clientOptions;
    this.#onError = onError;
  }

  #createClient() {
    if (this.#node === undefined) {
      throw new Error("pubSubProxy: didn't define node to do pubsub against");
    }

    return new RedisClient({
      ...this.#clientOptions,
      socket: {
        ...this.#clientOptions.socket,
        host: this.#node.host,
        port: this.#node.port
      }
    });
  }

  async #initiatePubSubClient(withSubscriptions = false) {
    const client = this.#createClient()
      .on('error', this.#onError);

    const connectPromise = client.connect()
      .then(async client => {

View on GitHub (pinned to 90fd0652bc)

Solutions

  1. `await sentinel.connect()` before issuing any pubsub command.
  2. Retry the subscribe if it fails during a failover/reset window.
  3. Ensure you route pubsub through the Sentinel's pubsub handle, not a raw client.

Example fix

// before
const s = new RedisSentinel(opts, id);
s.subscribe('ch', fn); // node not yet selected
// after
const s = new RedisSentinel(opts, id);
await s.connect();
s.subscribe('ch', fn);
Defensive patterns

Strategy: validation

Validate before calling

async function subscribeSafe(sentinel: { isReady: boolean; connect(): Promise<void>; subscribe(ch: string, fn: () => void): Promise<unknown> }, ch: string, fn: () => void) {
  if (!sentinel.isReady) await sentinel.connect();
  return sentinel.subscribe(ch, fn);
}

Type guard

function nodeSelected(proxy: { node?: unknown } | unknown): boolean {
  // PubSubProxy.#node is private; approximate by only subscribing after connect resolves
  return true;
}

Try / catch

async function subscribeWithRetry(sentinel: { subscribe(ch: string, fn: () => void): Promise<unknown>; isReady: boolean }, ch: string, fn: () => void, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try { return await sentinel.subscribe(ch, fn); }
    catch (e) {
      if (String(e).includes("didn't define node") && sentinel.isReady && i < attempts - 1) {
        await new Promise(r => setTimeout(r, 200)); continue;
      }
      throw e;
    }
  }
}

Prevention

When it happens

Trigger: Issuing SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE through the Sentinel before the proxy's node has been set — e.g. subscribing before `connect()` has selected a master/replica, or during the window after a failover reset before changeNode() reruns.

Common situations: Subscribing immediately after constructing the Sentinel without awaiting connect; race between reset (which clears state) and a subscribe call; internal failover timing.

Related errors


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