dgraph-io/dgraph · error
Unable to move reserved predicate %s
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.
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)
Solutions
- Do not move reserved predicates — they must stay where Dgraph assigns them
- Filter reserved predicates (dgraph.* and ACL/system predicates) out of any rebalancing script
- 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
- Exclude dgraph.* predicates from any rebalancing automation
- Never iterate 'all tablets from /state' without filtering reserved ones
- Rebalance via Zero's built-in rebalancer instead of manual moves where possible
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
- group: [%d] is not a known group
- namespace: %d. No tablet found for: %s
- namespace: %d. Tablet: [%s] is already being served by group
- Tablet to be moved: [%v] is not being served
- I am not the Zero leader
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/ef2f9577184d7973.
Report an issue: GitHub.