dgraph-io/dgraph · error

ACL predicates can't be deleted

Error message

ACL predicates can't be deleted

What it means

Error returned by the ACL layer in edgraph/access.go when a mutation attempts to delete predicates that are reserved for access control (e.g. dgraph.xid, dgraph.password, dgraph.user.group or other ACL-internal predicates). Fix by excluding ACL predicates from the deletion.

Source

Thrown at edgraph/access.go:819

	// as a byproduct, it also sets the userId and groups
	doAuthorizeMutation := func() error {
		userData, err := extractUserAndGroups(ctx)
		if err != nil {
			// We don't follow fail open approach anymore.
			return status.Error(codes.Unauthenticated, err.Error())
		}

		userId = userData.userId
		groupIds = userData.groupIds

		if x.IsSuperAdmin(groupIds) {
			// Members of guardians group are allowed to mutate anything
			// (including delete) except the permission of the acl predicates.
			switch {
			case isAclPredMutation(gmu.Set):
				return errors.Errorf("the permission of ACL predicates can not be changed")
			case isAclPredMutation(gmu.Del):
				return errors.Errorf("ACL predicates can't be deleted")
			}
			if !shouldAllowAcls(userData.namespace) {
				for _, pred := range preds {
					if x.IsAclPredicate(pred) {
						return status.Errorf(codes.PermissionDenied,
							"unauthorized to mutate acl predicates: %s\n", pred)
					}
				}
			}
			return nil
		}
		result := authorizePreds(ctx, userData, preds, acl.Write)
		if len(result.blocked) > 0 {
			var msg strings.Builder
			for key := range result.blocked {
				x.Check2(msg.WriteString(key))
				x.Check2(msg.WriteString(" "))
			}

View on GitHub (pinned to 759e242be6)

Solutions

  1. Narrow delete patterns to exclude ACL predicates (avoid `* * .` wildcards on ACL-owned nodes)
  2. Use `dgraph acl` CLI to manage/remove ACL rules instead of raw delete mutations
  3. List the exact predicates you intend to delete and verify none match x.IsAclPredicate
  4. For full resets, use DropAll as a guardian rather than targeted deletes

Example fix

// before
del.Nquads = []string{fmt.Sprintf("<%s> * * .", uid)} // can hit ACL preds
// after
del.Nquads = []string{fmt.Sprintf("<%s> <app.pred1> * .", uid), fmt.Sprintf("<%s> <app.pred2> * .", uid)}
Defensive patterns

Strategy: validation

Validate before calling

// Expand wildcard deletes explicitly and filter ACL predicates
const targets = ['app.pred1', 'app.pred2']
if (targets.some(p => p.startsWith('dgraph.'))) {
  throw new Error('refusing to delete ACL predicates')
}

Type guard

function isSafeDelete(preds) {
  return preds.every(p => !String(p).startsWith('dgraph.'))
}

Try / catch

try {
  await dg.mutate(delMutation)
} catch (e) {
  if (/ACL predicates can't be deleted/.test(e.message)) {
    // rewrite mutation without dgraph.* predicates and retry once
  }
  throw e
}

Prevention

When it happens

Trigger: A guardian sends a Del mutation whose predicates include ACL permission predicates (matched by isAclPredMutation(gmu.Del)), e.g. dropping nodes with wildcard predicate deletion that sweeps in dgraph.group.acl.

Common situations: Drop-by-uid wildcard deletes (`S * * .`) that unintentionally include ACL predicates; cleanup scripts purging all data for a namespace; copying delete logic from non-ACL clusters.

Related errors


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