dgraph-io/dgraph · error

I am not the Zero leader

Error message

I am not the Zero leader

What it means

movePredicate must run on the current Zero leader because it must propose the final tablet reassignment through the Zero raft group. After refreshing membership it checks s.Node.AmLeader(); if this Zero is a follower, it refuses with this error. Note MoveTablet checks leadership earlier too, but the rebalancer path (and a leadership change mid-request) can hit this inside movePredicate.

Source

Thrown at dgraph/cmd/zero/tablet.go:174

	// whose moves keep failing.
	start := time.Now()
	defer func() {
		s.recordMoveResult(predicate, time.Since(start), err)
	}()

	timeout := moveTimeout(predicateMoveTimeout, tab)
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	span := trace.SpanFromContext(ctx)
	defer span.End()

	// Ensure that I'm connected to the rest of the Zero group, and am the leader.
	if _, err := s.latestMembershipState(ctx); err != nil {
		return errors.Wrapf(err, "unable to reach quorum")
	}
	if !s.Node.AmLeader() {
		return errors.Errorf("I am not the Zero leader")
	}
	msg := fmt.Sprintf("Going to move predicate: [%v], size: [ondisk: %v, uncompressed: %v]"+
		" from group %d to %d, timeout: %v\n", predicate, humanize.IBytes(uint64(tab.OnDiskBytes)),
		humanize.IBytes(uint64(tab.UncompressedBytes)), srcGroup, dstGroup, timeout)
	glog.Info(msg)
	span.SetAttributes(attribute.String("tablet", predicate))
	span.SetStatus(1, msg)

	// Block all commits on this predicate. Keep them blocked until we return from this function.
	unblock := s.blockTablet(predicate)
	defer unblock()

	// Get a new timestamp, beyond which we are sure that no new txns would be committed for this
	// predicate. Source Alpha leader must reach this timestamp before streaming the data.
	ids, err := s.Timestamps(ctx, &pb.Num{Val: 1})
	if err != nil || ids.StartId == 0 {
		return errors.Wrapf(err, "while leasing txn timestamp. Id: %+v", ids)
	}

View on GitHub (pinned to 759e242be6)

Solutions

  1. Find the current Zero leader via /state and send the move request to it
  2. Configure the load balancer/proxy to route Zero writes to the leader only
  3. Wait for leadership to stabilize (check Zero logs for raft elections) and retry
  4. Investigate frequent leadership changes if this recurs — often network or disk latency between Zeros

Example fix

// before
curl 'zero-follower:6080/moveTablet?tablet=name&dst_group=2'
// after
LEADER=$(curl -s zero-follower:6080/state | jq -r '.leader // .max_leason_id' )
curl "zero-leader:6080/moveTablet?tablet=name&dst_group=2"
Defensive patterns

Strategy: retry

Validate before calling

# resolve current Zero leader and target it
leader=$(curl -s localhost:6080/state | jq -r '.leader // empty')
[ -n "$leader" ] || { echo "no zero leader elected"; exit 1; }

Try / catch

resp=$(curl -s "localhost:6080/moveTablet?tablet=$TABLET&dst_group=$DST")
if [[ "$(echo "$resp" | jq -r .msg)" == *"not the Zero leader"* ]]; then
  leader=$(curl -s localhost:6080/state | jq -r '.leader')
  resp=$(curl -s "http://$leader/moveTablet?tablet=$TABLET&dst_group=$DST")
fi

Prevention

When it happens

Trigger: The move was issued to (or rebalancer runs on) a Zero follower; Zero leadership changed after the request was accepted; multiple Zeros with stale views where a follower still believes it leads.

Common situations: Load balancer routing the /moveTablet HTTP call to any Zero instead of only the leader; scripted moves hard-coding a non-leader Zero address; leadership flapping due to unstable network between Zeros.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/c9c49ee0ccbbe579. Report an issue: GitHub.