{"record":{"id":"e23819fb341ca263","repo":"dgraph-io/dgraph","slug":"unable-to-unmarshal-acl-v-w","errorCode":null,"errorMessage":"unable to unmarshal ACL: %v :: %w","messagePattern":"unable to unmarshal ACL: (.+?) :: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upgrade/change_v20.03.0.go","lineNumber":62,"sourceCode":"\tif err := getQueryResult(dg, queryACLGroupsBefore_v20_03_0, &data); err != nil {\n\t\treturn fmt.Errorf(\"error querying old ACL rules: %w\", err)\n\t}\n\n\tgroups, ok := data[\"rules\"]\n\tif !ok {\n\t\treturn fmt.Errorf(\"unable to parse ACLs: %v\", data)\n\t}\n\n\tcounter := 1\n\tvar nquads []*api.NQuad\n\tfor _, group := range groups {\n\t\tif group.ACL == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar rs rules\n\t\tif err := json.Unmarshal([]byte(group.ACL), &rs); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to unmarshal ACL: %v :: %w\", group.ACL, err)\n\t\t}\n\n\t\tfor _, r := range rs {\n\t\t\tnewRuleStr := fmt.Sprintf(\"_:newrule%d\", counter)\n\t\t\tnquads = append(nquads, []*api.NQuad{\n\t\t\t\t// the name of the type was Rule in v20.03.0\n\t\t\t\tgetTypeNquad(newRuleStr, \"Rule\"),\n\t\t\t\t{\n\t\t\t\t\tSubject:   newRuleStr,\n\t\t\t\t\tPredicate: \"dgraph.rule.predicate\",\n\t\t\t\t\tObjectValue: &api.Value{\n\t\t\t\t\t\tVal: &api.Value_StrVal{StrVal: r.Predicate},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tSubject:   newRuleStr,\n\t\t\t\t\tPredicate: \"dgraph.rule.permission\",\n\t\t\t\t\tObjectValue: &api.Value{","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/upgrade/change_v20.03.0.go#L44-L80","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.).","solutions":["Inspect the ACL string printed in the error and fix invalid JSON directly in the graph before re-running the upgrade","Restore the affected dgraph.group.acl node from a backup taken before the corruption","Delete the malformed group ACL node if the group is no longer needed, then re-run the upgrade","Verify the JSON matches the old v20.03 Rule schema (permission as map of predicate->int32, etc.)"],"exampleFix":"// corrupt node in graph\n{\"rules\":[{\"permission\":{\"chat\":1},\"predicate\":\"chat\"} // truncated\n// after (fixed dgraph.group.acl JSON)\n{\"rules\":[{\"permission\":{\"chat\":1},\"predicate\":\"chat\"}]}","handlingStrategy":"validation","validationCode":"var probe map[string]json.RawMessage\nif err := json.Unmarshal([]byte(group.ACL), &probe); err != nil {\n    return fmt.Errorf(\"skip corrupt ACL JSON for group %s: %w\", group.Name, err)\n}","typeGuard":"func validACLJSON(s string) bool {\n    var rs rules\n    return s != \"\" && json.Unmarshal([]byte(s), &rs) == nil\n}","tryCatchPattern":"if err := upgradeACLRules(); err != nil {\n    if strings.Contains(err.Error(), \"unable to unmarshal ACL\") {\n        // extract the corrupt ACL from the message, fix/restore the node, rerun\n    }\n    return err\n}","preventionTips":["Never hand-edit dgraph.group.acl JSON via raw mutations; use the ACL API","Take a backup before upgrading so corrupt nodes can be restored","Validate stored ACL JSON (jq or json.Unmarshal probe) before running the migration"],"tags":["upgrade","acl","json","parsing","dgraph"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}