dgraph-io/dgraph · error

unable to unmarshal ACL: %v :: %w

Error message

unable to unmarshal ACL: %v :: %w

What it means

Each group's ACL field is a JSON document of old-style rules. upgradeACLRules unmarshals it into a rules struct; if the stored JSON is malformed or its shape changed (e.g. fields renamed between versions), json.Unmarshal fails and the error is wrapped with the offending ACL text and the underlying parse error.

Source

Thrown at upgrade/change_v20.03.0.go:62

	if err := getQueryResult(dg, queryACLGroupsBefore_v20_03_0, &data); err != nil {
		return fmt.Errorf("error querying old ACL rules: %w", err)
	}

	groups, ok := data["rules"]
	if !ok {
		return fmt.Errorf("unable to parse ACLs: %v", data)
	}

	counter := 1
	var nquads []*api.NQuad
	for _, group := range groups {
		if group.ACL == "" {
			continue
		}

		var rs rules
		if err := json.Unmarshal([]byte(group.ACL), &rs); err != nil {
			return fmt.Errorf("unable to unmarshal ACL: %v :: %w", group.ACL, err)
		}

		for _, r := range rs {
			newRuleStr := fmt.Sprintf("_:newrule%d", counter)
			nquads = append(nquads, []*api.NQuad{
				// the name of the type was Rule in v20.03.0
				getTypeNquad(newRuleStr, "Rule"),
				{
					Subject:   newRuleStr,
					Predicate: "dgraph.rule.predicate",
					ObjectValue: &api.Value{
						Val: &api.Value_StrVal{StrVal: r.Predicate},
					},
				},
				{
					Subject:   newRuleStr,
					Predicate: "dgraph.rule.permission",
					ObjectValue: &api.Value{

View on GitHub (pinned to 759e242be6)

Solutions

  1. Inspect the ACL string printed in the error and fix invalid JSON directly in the graph before re-running the upgrade
  2. Restore the affected dgraph.group.acl node from a backup taken before the corruption
  3. Delete the malformed group ACL node if the group is no longer needed, then re-run the upgrade
  4. Verify the JSON matches the old v20.03 Rule schema (permission as map of predicate->int32, etc.)

Example fix

// corrupt node in graph
{"rules":[{"permission":{"chat":1},"predicate":"chat"} // truncated
// after (fixed dgraph.group.acl JSON)
{"rules":[{"permission":{"chat":1},"predicate":"chat"}]}
Defensive patterns

Strategy: validation

Validate before calling

var probe map[string]json.RawMessage
if err := json.Unmarshal([]byte(group.ACL), &probe); err != nil {
    return fmt.Errorf("skip corrupt ACL JSON for group %s: %w", group.Name, err)
}

Type guard

func validACLJSON(s string) bool {
    var rs rules
    return s != "" && json.Unmarshal([]byte(s), &rs) == nil
}

Try / catch

if err := upgradeACLRules(); err != nil {
    if strings.Contains(err.Error(), "unable to unmarshal ACL") {
        // extract the corrupt ACL from the message, fix/restore the node, rerun
    }
    return err
}

Prevention

When it happens

Trigger: A dgraph.group.acl node whose ACL JSON was hand-edited, truncated, or written by an incompatible Dgraph version, so json.Unmarshal([]byte(group.ACL), &rs) fails during the 20.03 upgrade.

Common situations: Clusters where ACL rules were modified directly via mutations bypassing the ACL API; partial writes from a crashed earlier upgrade; custom rule objects that don't match the expected old Rule schema (permission maps, etc.).

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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