{"id":"1aadfdc2a6edf07d","repo":"redis/node-redis","slug":"no-valid-master-node","errorCode":null,"errorMessage":"no valid master node","messagePattern":"no valid master node","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/sentinel/index.ts","lineNumber":1371,"sourceCode":"        this.#trace(`observe: error ${err}`);\n        this.emit('error', err);\n      } finally {\n        if (client !== undefined && client.isOpen) {\n          this.#trace(`observe: destroying sentinel client`);\n          client.destroy();\n        }\n      }\n    }\n\n    this.#trace(`observe: none of the sentinels are available`);\n    throw new Error('None of the sentinels are available');\n  }\n\n  analyze(observed: Awaited<ReturnType<RedisSentinelInternal<M, F, S, RESP, TYPE_MAPPING>[\"observe\"]>>) {\n    let master = parseNode(observed.masterData);\n    if (master === undefined) {\n      this.#trace(`analyze: no valid master node because ${observed.masterData.flags}`);\n      throw new Error(\"no valid master node\");\n    }\n\n    if (master.host === observed.currentMaster?.host && master.port === observed.currentMaster?.port) {\n      this.#trace(`analyze: master node hasn't changed from ${observed.currentMaster?.host}:${observed.currentMaster?.port}`);\n      master = undefined;\n    } else {\n      this.#trace(`analyze: master node has changed to ${master.host}:${master.port} from ${observed.currentMaster?.host}:${observed.currentMaster?.port}`);\n    }\n\n    let sentinel: RedisNode | undefined = observed.sentinelConnected;\n    if (sentinel.host === observed.currentSentinel?.host && sentinel.port === observed.currentSentinel.port) {\n      this.#trace(`analyze: sentinel node hasn't changed`);\n      sentinel = undefined;\n    } else {\n      this.#trace(`analyze: sentinel node has changed to ${sentinel.host}:${sentinel.port}`);\n    }\n\n    const replicasToClose: Array<RedisNode> = [];","sourceCodeStart":1353,"sourceCodeEnd":1389,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/sentinel/index.ts#L1353-L1389","documentation":"Thrown by analyze() when parseNode(observed.masterData) returns undefined (sentinel/index.ts:1369). parseNode extracts host/port from the SENTINEL MASTER reply and rejects entries whose flags indicate the node is not a usable master; the trace logs `because ${observed.masterData.flags}`. So a sentinel was reached and replied, but the master it reported could not be parsed into a valid master node.","triggerScenarios":"The monitored name (options.name) does not match any master the sentinel monitors (SENTINEL MASTER returns an error/empty shape); the master is flagged down/disconnected in a way parseNode rejects; a sentinel version returning a malformed master record.","commonSituations":"Typo in options.name vs the actual master name configured in sentinel.conf; pointing at a sentinel that monitors a different deployment; sentinel mid-failover where the master record is transitional.","solutions":["Confirm options.name exactly matches the master name in `sentinel masters` output on your sentinel.","Run `redis-cli -h <sentinel> -p 26379 sentinel master <name>` and inspect the flags/host/port fields.","If the master is flagged down, wait for failover to elect a new master and let the retry loop (maxCommandRediscovers) recover.","Point sentinelRootNodes at sentinels that actually monitor the named master."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate the master name against a reachable sentinel before connecting.\nimport { createClient } from '@redis/client';\nasync function assertMasterMonitored(sentinelNode, name) {\n  const c = createClient({ socket: sentinelNode, modules: undefined });\n  c.on('error', () => {});\n  await c.connect();\n  try {\n    const masters = await c.sentinel.sentinelMasters();\n    if (!masters.some(m => m.name === name)) {\n      throw new Error(`sentinel does not monitor master '${name}'`);\n    }\n  } finally {\n    await c.destroy();\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await sentinel.connect();\n} catch (e) {\n  if (e instanceof Error && /no valid master node/.test(e.message)) {\n    // options.name is wrong or master is failing over; surface to operator\n  }\n  throw e;\n}","preventionTips":["Treat options.name as a checked constant derived from sentinel.conf, not free text.","Log/inspect observed.masterData.flags in an 'error' handler to diagnose.","Allow the bounded retry loop (maxCommandRediscovers) to ride out failovers before surfacing."],"tags":["sentinel","configuration","master","topology"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}