{"record":{"id":"c7d486bcb2d0356c","repo":"dgraph-io/dgraph","slug":"create-proposal-invalid-group-v","errorCode":null,"errorMessage":"Create Proposal: Invalid group: %+v","messagePattern":"Create Proposal: Invalid group: %\\+v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dgraph/cmd/zero/zero.go","lineNumber":351,"sourceCode":"\t_, blocked := s.blockCommitsOn.Load(pred)\n\treturn blocked\n}\n\nfunc (s *Server) servingTablet(tablet string) *pb.Tablet {\n\ts.AssertRLock()\n\n\tfor _, group := range s.state.Groups {\n\t\tif tab, ok := group.Tablets[tablet]; ok {\n\t\t\treturn tab\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *Server) createProposals(dst *pb.Group) ([]*pb.ZeroProposal, error) {\n\tvar res []*pb.ZeroProposal\n\tif len(dst.Members) > 1 {\n\t\treturn res, errors.Errorf(\"Create Proposal: Invalid group: %+v\", dst)\n\t}\n\n\ts.RLock()\n\tdefer s.RUnlock()\n\t// There is only one member. We use for loop because we don't know what the mid is.\n\tfor mid, dstMember := range dst.Members {\n\t\tgroup, has := s.state.Groups[dstMember.GroupId]\n\t\tif !has {\n\t\t\treturn res, errors.Errorf(\"Unknown group for member: %+v\", dstMember)\n\t\t}\n\t\tsrcMember, has := group.Members[mid]\n\t\tif !has {\n\t\t\treturn res, errors.Errorf(\"Unknown member: %+v\", dstMember)\n\t\t}\n\t\tif srcMember.Addr != dstMember.Addr ||\n\t\t\tsrcMember.Leader != dstMember.Leader {\n\n\t\t\tproposal := &pb.ZeroProposal{","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/dgraph/cmd/zero/zero.go#L333-L369","documentation":"createProposals validates an incoming membership state (dst) before turning it into raft proposals. A valid group must have exactly one member, so a reported group with more than one member is rejected as invalid.","triggerScenarios":"UpdateMembership receiving a pb.Group whose Members map contains 2+ entries — normally impossible since each group has one Alpha leader entry.","commonSituations":"A buggy or malicious Alpha sending fabricated membership state; protocol/version mismatch producing malformed ConnectionState updates; custom tooling crafting zero.pb requests directly.","solutions":["Identify which Alpha sent the malformed group state (log the dst group) and check its version/health.","Restart the offending Alpha so it re-registers with correct single-member state.","Upgrade Alphas and Zero to matching dgraph versions (v25.x).","Do not hand-craft membership protobufs; use official client endpoints."],"exampleFix":"// before\nproposal, err := zero.UpdateMembership(ctx, malformedState)\n// after\nif len(group.Members) > 1 {\n    return fmt.Errorf(\"skipping invalid group %d with %d members\", group.GroupId, len(group.Members))\n}\nproposal, err := zero.UpdateMembership(ctx, state)","handlingStrategy":"validation","validationCode":"if group == nil || len(group.Members) != 1 {\n    return fmt.Errorf(\"refusing to send group %d with %d members\", group.GetGroupId(), len(group.GetMembers()))\n}","typeGuard":"func isValidGroup(g *pb.Group) bool {\n    return g != nil && len(g.Members) == 1\n}","tryCatchPattern":"proposals, err := s.createProposals(dst)\nif err != nil && strings.Contains(err.Error(), \"Invalid group\") {\n    log.Warnf(\"dropping malformed membership update: %v\", err)\n    return nil\n}","preventionTips":["Never hand-craft membership protobufs; use Alpha's official heartbeat/UpdateMembership path.","Keep all nodes on the same dgraph version to avoid serialization mismatches.","Log and drop malformed membership states at the client before sending.","Audit any middleware that mutates pb.Group structures."],"tags":["zero","membership","validation","raft"],"backgroundTag":"invalid-membership-state","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}