{"record":{"id":"b24336278484513b","repo":"nats-io/nats-server","slug":"w-for-stream-s-s","errorCode":null,"errorMessage":"%w for stream '%s > %s'","messagePattern":"%w for stream '(.+?) > (.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/jetstream_cluster.go","lineNumber":12496,"sourceCode":"\tconst maxRetries = 3\n\tvar numRetries int\n\nRETRY:\n\t// On retry, we need to release the semaphore we got. Call will be no-op\n\t// if releaseSem boolean has not been set to true on successfully getting\n\t// the semaphore.\n\treleaseSyncOutSem()\n\n\tif n.Leaderless() {\n\t\t// Prevent us from spinning if we've installed a snapshot from a leader but there's no leader online.\n\t\t// We wait a bit to check if a leader has come online in the meantime, if so we can continue.\n\t\tvar canContinue bool\n\t\tif numRetries == 0 {\n\t\t\ttime.Sleep(startInterval)\n\t\t\tcanContinue = !n.Leaderless()\n\t\t}\n\t\tif !canContinue {\n\t\t\treturn fmt.Errorf(\"%w for stream '%s > %s'\", errCatchupAbortedNoLeader, mset.account(), mset.name())\n\t\t}\n\t}\n\n\t// If we have a sub clear that here.\n\tif sub != nil {\n\t\ts.sysUnsubscribe(sub)\n\t\tsub = nil\n\t}\n\n\tif !s.isRunning() {\n\t\treturn ErrServerNotRunning\n\t}\n\n\tnumRetries++\n\tif numRetries > maxRetries {\n\t\t// Force a hard reset here.\n\t\treturn errCatchupTooManyRetries\n\t}","sourceCodeStart":12478,"sourceCodeEnd":12514,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/jetstream_cluster.go#L12478-L12514","documentation":"When a stream message consumer/catch-up routine (processInboundMsg on a mirror/source or raft catch-up path) is running and the raft/node loses its leader, it retries with a sleep interval. If the group is still leaderless on the first retry (numRetries == 0 and n.Leaderless()), it aborts with fmt.Errorf(\"%w for stream '%s > %s'\", errCatchupAbortedNoLeader, ...), wrapping errCatchupAbortedNoLeader so callers can detect the catch-up was aborted because there is no raft leader to sync from.","triggerScenarios":"A stream replica (or mirror/source consumer) attempts to catch up while the stream's raft group has no elected leader — e.g. the leader just crashed, a majority of peers are down, or a rolling restart removed the leader before a new election completed.","commonSituations":"Clusters operating with degraded quorum; maintenance windows where multiple JetStream servers restart simultaneously; network partitions isolating the leader.","solutions":["Restore quorum: bring the required number of JetStream servers back online so a leader can be elected.","Check `nats server request JetStream` / `nats str info <stream>` for peer/leader status and fix any partitioned node.","After quorum is restored the catch-up retries; if it stays aborted, restart the affected replica server.","Space out rolling restarts so the leader is never removed before a new one is elected."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// before starting mirrors/sources or relying on catch-up, check leader presence\nsi, _ := js.StreamInfo(name)\nif si.Cluster != nil && si.Cluster.Leader == \"\" {\n    // leaderless: wait before triggering work that requires catch-up\n}","typeGuard":null,"tryCatchPattern":"err := doCatchup()\nif errors.Is(err, errCatchupAbortedNoLeader) {\n    // exponential backoff until the group elects a leader\n    retryWithBackoff(doCatchup)\n}","preventionTips":["Maintain quorum: never take down a majority of JetStream peers at once.","Sequence rolling restarts so a new leader is elected between restarts.","Alert on leaderless streams via stream info cluster.leader.","Monitor network partitions and raft election timeouts."],"tags":["jetstream","raft","leaderless","catchup","cluster"],"backgroundTag":"catchup-aborted-no-leader","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}