redis/node-redis · error · Error

SCAN: no master nodes available

Error message

SCAN: no master nodes available

What it means

Thrown by routeScan when a cluster-wide SCAN starts (cursor '0') but slots.nextScanTarget(EMPTY_VISITED) returns nothing, meaning the cluster topology currently exposes zero master nodes capable of serving the scan. The router cannot begin the per-node walk because it has nowhere to send the first SCAN.

Source

Thrown at packages/client/lib/cluster/request-response-policies/scan-cursor.ts:40

 *    finally sees "0".
 *
 * The visited set is tracked by node address, so a topology change mid-scan
 * neither rescans a surviving node nor gets stuck on a departed one. The usual
 * SCAN guarantees apply per node; keys migrating between nodes mid-iteration
 * may be missed or duplicated — same caveat as every cluster-wide scan.
 */
export const routeScan: RequestRouter = async (slots, parser) => {
  // Malformed raw command (missing cursor): forward to any node so the server
  // returns its own arity error instead of a client-side TypeError.
  if (parser.redisArgs.length < 2) {
    return [{ client: await slots.nodeClient(slots.getRandomNode()) }];
  }

  const cursorArg = argToString(parser.redisArgs[1]);

  if (cursorArg === '0') {
    const address = slots.nextScanTarget(EMPTY_VISITED);
    if (!address) throw new Error('SCAN: no master nodes available');
    return [{ client: await pinnedMaster(slots, address) }];
  }

  const entry = slots.lookupScanCursor(cursorArg);
  if (!entry) {
    throw new Error(
      `SCAN: unknown cursor "${cursorArg}". Cluster-wide SCAN cursors are ` +
      `minted per client instance and expire when idle — restart the scan from 0.`
    );
  }
  return [{
    client: await pinnedMaster(slots, entry.address),
    parser: withCursor(parser, entry.cursor)
  }];
};

const EMPTY_VISITED: ReadonlySet<string> = new Set();

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Ensure await client.connect() has resolved before issuing SCAN — check client.isReady.
  2. Retry the scan after a short delay if the cluster is mid-failover; start a fresh scan from cursor 0.
  3. Verify the cluster is healthy: run CLUSTER NODES / CLUSTER SLOTS against a seed node and confirm masters are online.
  4. Confirm you created the client with createCluster() pointing at real cluster nodes, not a single standalone Redis.

Example fix

// before
const cluster = createCluster(...);
cluster.scan(0); // topology not yet populated

// after
await cluster.connect();
await cluster.scan(0);
Defensive patterns

Strategy: validation

Validate before calling

if (!cluster.isReady) throw new Error('cluster not ready');
// Optionally probe topology first:
// const nodes = await cluster.clusterNodes();

Type guard

function clusterReady(c) { return typeof c.isReady === 'boolean' && c.isReady; }

Try / catch

try { return await cluster.scan(cursor); } catch (e) { if (/no master nodes available/.test(e.message)) { await delay(500); return cluster.scan('0'); } throw e; }

Prevention

When it happens

Trigger: Calling client.scan(0) (or any scanIterator on a cluster client) while the cluster slots map is empty, still loading, or every shard is in a fail/downtime state. Also reachable if the cluster client is not yet connected/isReady when the first SCAN is issued.

Common situations: Issuing SCAN before await client.connect() resolves on a cluster; cluster in the middle of a full failover or resharding where the slots view is momentarily empty; misconfigured cluster where CLUSTER SLOTS returns no nodes; connecting to a non-cluster endpoint in cluster mode.

Related errors


AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03). Data as JSON: /data/errors/2110222c4fe05c5c.json. Report an issue: GitHub.