dgraph-io/dgraph · error
No group with groupId %d found
Error message
No group with groupId %d found
What it means
RemoveNode validates that the group id supplied in the request exists in Zero's current cluster state before proposing the node's removal. If s.state.Groups has no entry for req.GroupId, the removal is aborted with this error.
Source
Thrown at dgraph/cmd/zero/zero.go:486
tablets = append(tablets, tab)
}
return &pb.TabletResponse{
Tablets: tablets,
}, nil
}
// RemoveNode removes the given node from the given group.
// It's the user's responsibility to ensure that node doesn't come back again
// before calling the api.
func (s *Server) RemoveNode(ctx context.Context, req *pb.RemoveNodeRequest) (*pb.Status, error) {
if req.GroupId == 0 {
return nil, s.Node.ProposePeerRemoval(ctx, req.NodeId)
}
zp := &pb.ZeroProposal{}
zp.Member = &pb.Member{Id: req.NodeId, GroupId: req.GroupId, AmDead: true}
if _, ok := s.state.Groups[req.GroupId]; !ok {
return nil, errors.Errorf("No group with groupId %d found", req.GroupId)
}
if _, ok := s.state.Groups[req.GroupId].Members[req.NodeId]; !ok {
return nil, errors.Errorf("No node with nodeId %d found in group %d", req.NodeId,
req.GroupId)
}
if len(s.state.Groups[req.GroupId].Members) == 1 && len(s.state.Groups[req.GroupId].
Tablets) > 0 {
return nil, errors.Errorf("Move all tablets from group %d before removing the last node",
req.GroupId)
}
if err := s.Node.proposeAndWait(ctx, zp); err != nil {
return nil, err
}
return &pb.Status{}, nil
}
// Connect is used by Alpha nodes to connect the very first time with group zero.View on GitHub (pinned to 759e242be6)
Solutions
- Fetch the current cluster state (curl localhost:6080/state) and use an existing GroupId that actually contains the node.
- If the whole group is gone, no removal is needed — skip the call or remove via ProposePeerRemoval (omit GroupId so req.GroupId==0 path handles the node globally).
- Re-check the node id/group mapping with dgraph zero tools before re-running the command.
- Refresh Zero state / restart Zero so its membership reflects reality, then retry.
Example fix
// before
removeNode(zeroAddr, nodeID, 7) // group 7 no longer exists
// after
state := fetchZeroState(zeroAddr)
group := findGroupContainingNode(state, nodeID)
if group == 0 {
removeNode(zeroAddr, nodeID, 0) // peer removal path
} else {
removeNode(zeroAddr, nodeID, group)
} Defensive patterns
Strategy: validation
Validate before calling
state := fetchZeroState(zeroAddr)
if _, ok := state.Groups[groupId]; !ok {
return fmt.Errorf("group %d does not exist; refresh state before removing node", groupId)
} Type guard
func groupExists(state *pb.ClusterState, gid uint64) bool {
_, ok := state.Groups[gid]
return ok
} Try / catch
resp, err := zero.RemoveNode(ctx, req)
if err != nil && strings.Contains(err.Error(), "No group with groupId") {
state := fetchZeroState(zeroAddr)
req.GroupId = lookupGroupForNode(state, req.NodeId)
resp, err = zero.RemoveNode(ctx, req)
} Prevention
- Always pull /state immediately before removeNode to get fresh group ids.
- Script removals from parsed /state output, not hard-coded ids.
- Re-check after cluster rebuilds or restores since group numbering can change.
- Use the GroupId==0 peer-removal path when only the node id is known.
When it happens
Trigger: Calling /removeNode (or RemoveNode via TestRemoveNode) with a GroupId that doesn't exist — typo'd group id, a group already removed, or group id taken from stale /state output.
Common situations: Operator removing a dead Alpha using an outdated cluster listing; group numbers shifted after a cluster rebuild; running removeNode against a freshly restarted Zero with restored/older state.
Related errors
- No node with nodeId %d found in group %d
- while proposing tablet reassignment. Proposal: %+v
- Create Proposal: Invalid group: %+v
- Unknown group for member: %+v
- Unknown member: %+v
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/c294e5e0bdc91bfe.
Report an issue: GitHub.