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
- `await sentinel.connect()` before issuing any pubsub command.
- Retry the subscribe if it fails during a failover/reset window.
- 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
- Always `await sentinel.connect()` before any pubsub command.
- Retry pubsub setup if it lands in a failover/reset window.
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
- Attempted execution on released RedisSentinelClient lease
- RedisSentinelClient lease already released
- already attempting to open
- TokenManager is not running, but refresh was called
- TokenManager is not running, but a new token was received
AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11).
Data as JSON: /api/errors/fbcf2d381cc78c16.
Report an issue: GitHub.