nats-io/nats-server · error · JSClusterNoPeersError

10005

10005

Error message

preferred server %q is already leader

What it means

JetStream cluster placement error (JSClusterNoPeersError, code 10005). When a stream/mirror/copy placement specifies a Preferred server, the server checks whether that peer is already the RAFT group leader. If the preferred server is already leader, it cannot be re-elected as a new leader via this operation, so the request fails.

Source

Thrown at server/jetstream_api.go:3365

// should check for return API errors and return those to the requestor if needed.
func (s *Server) getStepDownPreferredPlacement(group RaftNode, placement *Placement) (string, *ApiError) {
	if placement == nil {
		return _EMPTY_, nil
	}
	var preferredLeader string
	if placement.Preferred != _EMPTY_ {
		for _, p := range group.Peers() {
			si, ok := s.nodeToInfo.Load(p.ID)
			if !ok || si == nil {
				continue
			}
			if si.(nodeInfo).name == placement.Preferred {
				preferredLeader = p.ID
				break
			}
		}
		if preferredLeader == group.ID() {
			return _EMPTY_, NewJSClusterNoPeersError(fmt.Errorf("preferred server %q is already leader", placement.Preferred))
		}
		if preferredLeader == _EMPTY_ {
			return _EMPTY_, NewJSClusterNoPeersError(fmt.Errorf("preferred server %q not known", placement.Preferred))
		}
	} else {
		possiblePeers := make(map[*Peer]nodeInfo, len(group.Peers()))
		ourID := group.ID()
		for _, p := range group.Peers() {
			if p == nil {
				continue // ... shouldn't happen.
			}
			si, ok := s.nodeToInfo.Load(p.ID)
			if !ok || si == nil {
				continue
			}
			ni := si.(nodeInfo)
			if ni.offline || p.ID == ourID {
				continue

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check the current leader first; skip the stepdown if the preferred server already leads
  2. Issue the stepdown without the Preferred field to let the cluster choose a new leader
  3. Make automation idempotent: treat this error as success (desired state already achieved)

Example fix

// before
jsm stream leader stepdown STREAM --preferred-server=nats-3
// after
LEADER=$(jsm stream info STREAM -j | jq -r .cluster.leader)
[ "$LEADER" = "nats-3" ] || jsm stream leader stepdown STREAM --preferred-server=nats-3
Defensive patterns

Strategy: validation

Validate before calling

leader := getStreamLeader(streamName)
if leader == preferredServer {
	return // already desired state, skip stepdown
}

Type guard

func stepdownNeeded(currentLeader, preferred string) bool { return currentLeader != preferred }

Try / catch

err := stepdown(stream, preferred)
var apiErr *jsm.APIError
if errors.As(err, &apiErr) && strings.Contains(apiErr.Description, "is already leader") {
	// treat as success: preferred server already leads
}

Prevention

When it happens

Trigger: Issuing a leader-stepdown or stream re-placement API request (e.g. jsm stream leader stepdown --preferred-server=X, or the JS API leader stepdown endpoint) where server X is already the current leader of the group.

Common situations: Automation/scheduler scripts that run stepdown with a preferred target without first checking the current leader; re-running a failed maintenance job after the preferred server already became leader.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/da5868263f4def93. Report an issue: GitHub.