{"id":"2110222c4fe05c5c","repo":"redis/node-redis","slug":"scan-no-master-nodes-available","errorCode":null,"errorMessage":"SCAN: no master nodes available","messagePattern":"SCAN: no master nodes available","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/request-response-policies/scan-cursor.ts","lineNumber":40,"sourceCode":" *    finally sees \"0\".\n *\n * The visited set is tracked by node address, so a topology change mid-scan\n * neither rescans a surviving node nor gets stuck on a departed one. The usual\n * SCAN guarantees apply per node; keys migrating between nodes mid-iteration\n * may be missed or duplicated — same caveat as every cluster-wide scan.\n */\nexport const routeScan: RequestRouter = async (slots, parser) => {\n  // Malformed raw command (missing cursor): forward to any node so the server\n  // returns its own arity error instead of a client-side TypeError.\n  if (parser.redisArgs.length < 2) {\n    return [{ client: await slots.nodeClient(slots.getRandomNode()) }];\n  }\n\n  const cursorArg = argToString(parser.redisArgs[1]);\n\n  if (cursorArg === '0') {\n    const address = slots.nextScanTarget(EMPTY_VISITED);\n    if (!address) throw new Error('SCAN: no master nodes available');\n    return [{ client: await pinnedMaster(slots, address) }];\n  }\n\n  const entry = slots.lookupScanCursor(cursorArg);\n  if (!entry) {\n    throw new Error(\n      `SCAN: unknown cursor \"${cursorArg}\". Cluster-wide SCAN cursors are ` +\n      `minted per client instance and expire when idle — restart the scan from 0.`\n    );\n  }\n  return [{\n    client: await pinnedMaster(slots, entry.address),\n    parser: withCursor(parser, entry.cursor)\n  }];\n};\n\nconst EMPTY_VISITED: ReadonlySet<string> = new Set();\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/scan-cursor.ts#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure await client.connect() has resolved before issuing SCAN — check client.isReady.","Retry the scan after a short delay if the cluster is mid-failover; start a fresh scan from cursor 0.","Verify the cluster is healthy: run CLUSTER NODES / CLUSTER SLOTS against a seed node and confirm masters are online.","Confirm you created the client with createCluster() pointing at real cluster nodes, not a single standalone Redis."],"exampleFix":"// before\nconst cluster = createCluster(...);\ncluster.scan(0); // topology not yet populated\n\n// after\nawait cluster.connect();\nawait cluster.scan(0);","handlingStrategy":"validation","validationCode":"if (!cluster.isReady) throw new Error('cluster not ready');\n// Optionally probe topology first:\n// const nodes = await cluster.clusterNodes();","typeGuard":"function clusterReady(c) { return typeof c.isReady === 'boolean' && c.isReady; }","tryCatchPattern":"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; }","preventionTips":["Always await client.connect() before issuing SCAN on a cluster.","Check client.isReady before long scans.","Monitor cluster health (CLUSTER NODES) before large key walks."],"tags":["cluster","scan","topology","connection-state"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}