dgraph-io/dgraph · info

namespace: %d. Tablet: [%s] is already being served by group

Error message

namespace: %d. Tablet: [%s] is already being served by group: [%d]

What it means

MoveTablet reads the group currently serving the tablet (tab.GroupId) and compares it to the requested destination group. If they are equal, the move is a no-op, so Zero short-circuits with an invalid-request Status rather than performing a pointless move cycle. The error is purely informational — the tablet is already in the requested state.

Source

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

			break
		}
	}
	if !isKnown {
		return &pb.Status{Code: 1, Msg: x.ErrorInvalidRequest},
			fmt.Errorf("group: [%d] is not a known group", req.DstGroup)
	}

	tablet := x.NamespaceAttr(req.Namespace, req.Tablet)
	tab := s.ServingTablet(tablet)
	if tab == nil {
		return &pb.Status{Code: 1, Msg: x.ErrorInvalidRequest},
			fmt.Errorf("namespace: %d. No tablet found for: %s", req.Namespace, req.Tablet)
	}

	srcGroup := tab.GroupId
	if srcGroup == req.DstGroup {
		return &pb.Status{Code: 1, Msg: x.ErrorInvalidRequest},
			fmt.Errorf("namespace: %d. Tablet: [%s] is already being served by group: [%d]",
				req.Namespace, req.Tablet, srcGroup)
	}

	if err := s.movePredicate(tablet, srcGroup, req.DstGroup); err != nil {
		glog.Errorf("namespace: %d. While moving predicate %s from %d -> %d. Error: %v",
			req.Namespace, req.Tablet, srcGroup, req.DstGroup, err)
		return &pb.Status{Code: 1, Msg: x.Error}, err
	}

	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) {

View on GitHub (pinned to 759e242be6)

Solutions

  1. No action needed — the tablet is already served by the requested group; verify with /state
  2. If the goal was to move it elsewhere, pass a different dst_group
  3. Make automation scripts compare tab.GroupId to dst_group and skip no-op moves

Example fix

// before
curl 'localhost:6080/moveTablet?tablet=name&dst_group=2'  // group 2 already serves it
// after
STATE=$(curl -s localhost:6080/state)
CUR=$(echo "$STATE" | jq '.groups["2"].tablets.name.groupId')
[ "$CUR" != 2 ] && curl 'localhost:6080/moveTablet?tablet=name&dst_group=2'
Defensive patterns

Strategy: validation

Validate before calling

state=$(curl -s localhost:6080/state)
cur=$(echo "$state" | jq -r --arg p "$TABLET" '.groups | to_entries[] | select(.value.tablets[$p]) | .key')
if [ "$cur" = "$DST_GROUP" ]; then echo "already in group $DST_GROUP, skipping"; exit 0; fi

Type guard

move_is_noop() {
  local cur
  cur=$(curl -s localhost:6080/state | jq -r --arg p "$1" '.groups | to_entries[] | select(.value.tablets[$p]) | .key')
  [ "$cur" = "$2" ]
}

Try / catch

resp=$(curl -s "localhost:6080/moveTablet?tablet=$TABLET&dst_group=$DST_GROUP")
case $(echo "$resp" | jq -r .msg) in
  *"already being served"*) echo "no-op move, ignoring";; *) echo "move result: $resp";; esac

Prevention

When it happens

Trigger: Calling /moveTablet with dst_group equal to the group already serving the predicate — e.g. re-running a completed move command, or a script computing destination from the same membership snapshot used to pick the source.

Common situations: Idempotent re-run of an automation script after the first move succeeded; operator copied the source group into the destination field; a prior rebalancer already moved the tablet to the target group.

Related errors


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