{"record":{"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/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/cluster/request-response-policies/scan-cursor.ts#L22-L58","documentation":"A cluster-wide SCAN starting from cursor '0' could not find any master node to route to. The cluster slots state has no known master addresses, so nextScanTarget returns undefined. This means the client has no topology or all nodes are unreachable.","triggerScenarios":"Calling client.scan() or iterating scanIterator in cluster mode when the slots table has no master node addresses — the cluster is down, the client hasn't completed slots discovery, or all masters were removed from the topology.","commonSituations":"Cluster is down or unreachable; client connected but slots refresh hasn't completed; all master nodes removed during a major reshard; network partition isolating all nodes.","solutions":["Verify the cluster is healthy and at least one node is reachable via redis-cli","Wait for the client to finish its initial slots discovery before starting a scan","Check connectivity with a simple command (e.g., client.ping()) before initiating a scan","Implement retry logic with backoff to wait for topology recovery"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check cluster health before scanning.\ntry {\n  await client.ping();\n} catch {\n  throw new Error('Cluster is not reachable — cannot start SCAN');\n}","typeGuard":null,"tryCatchPattern":"async function scanWithRetry(client, fn, maxRetries = 3) {\n  for (let i = 0; i < maxRetries; i++) {\n    try {\n      for await (const keys of client.scanIterator()) {\n        await fn(keys);\n      }\n      return;\n    } catch (e) {\n      if (e.message === 'SCAN: no master nodes available' && i < maxRetries - 1) {\n        await new Promise(r => setTimeout(r, 1000 * (i + 1)));\n        continue;\n      }\n      throw e;\n    }\n  }\n}","preventionTips":["Verify cluster connectivity before starting a scan","Ensure the client has completed its initial slots refresh before scanning","Implement retry with exponential backoff for transient cluster unavailability"],"tags":["cluster","scan","topology","availability"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}