dgraph-io/dgraph · error

Unable to move reserved predicate

Error message

Unable to move reserved predicate %s

What it means

movePredicate (used by both the automatic rebalancer and MoveTablet) refuses to move predicates that Dgraph reserves for internal use, detected via x.IsReservedPredicate. Reserved predicates (e.g. internal/system predicates like dgraph.type, dgraph.xid, ACL predicates) are managed by the system and moving them would corrupt internal state, so the move is rejected unconditionally.

Solutions

  1. Do not move reserved predicates — they must stay where Dgraph assigns them
  2. Filter reserved predicates (dgraph.* and ACL/system predicates) out of any rebalancing script
  3. Rebalance only user-defined predicates listed in the schema

Example fix

// before
curl 'localhost:6080/moveTablet?tablet=dgraph.type&dst_group=2'
// after
PREDICATES=$(curl -s localhost:6080/state | jq -r '.groups[].tablets | keys[]' | grep -v '^dgraph\.')
for p in $PREDICATES; do curl "localhost:6080/moveTablet?tablet=$p&dst_group=2"; done
Defensive patterns

Strategy: validation

Validate before calling

case "$TABLET" in
  dgraph.*|""*) echo "$TABLET is a reserved predicate and cannot be moved"; exit 1;;
esac

Type guard

is_movable() {
  case "$1" in dgraph.*) return 1;; *) return 0;; esac
}

Try / catch

resp=$(curl -s "localhost:6080/moveTablet?tablet=$TABLET&dst_group=$DST")
[[ "$(echo "$resp" | jq -r .msg)" == *"reserved predicate"* ]] && echo "skipping reserved predicate $TABLET"

Prevention

When it happens

Trigger: Explicitly requesting a move for a reserved predicate via /moveTablet (e.g. tablet=dgraph.type), or the rebalancer somehow selecting one (normally filtered out earlier).

Common situations: Operators trying to manually rebalance system predicates to shrink a hot group; scripts iterating over all predicates from /state including reserved ones; misunderstanding that internal predicates are movable like user predicates.

Related errors


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

Appendix: source

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

	}

	return &pb.Status{Code: 0, Msg: fmt.Sprintf("namespace: %d. "+
		"Predicate: [%s] moved from group [%d] to [%d]", req.Namespace, req.Tablet, srcGroup,
		req.DstGroup)}, nil
}

// movePredicate is the main entry point for move predicate logic. This Zero must remain the leader
// for the entire duration of predicate move. If this Zero stops being the leader, the final
// proposal of reassigning the tablet to the destination would fail automatically.
func (s *Server) movePredicate(predicate string, srcGroup, dstGroup uint32) (err error) {
	s.moveOngoing <- struct{}{}
	defer func() {
		<-s.moveOngoing
	}()

	// Ensure that reserved predicates cannot be moved.
	if x.IsReservedPredicate(predicate) {
		return errors.Errorf("Unable to move reserved predicate %s", predicate)
	}
	tab := s.ServingTablet(predicate)
	if tab == nil {
		return errors.Errorf("Tablet to be moved: [%v] is not being served", predicate)
	}

	// Feed the outcome of this attempt back to the rebalancer, so it stops re-picking a tablet
	// 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)

View on GitHub (pinned to 759e242be6)