{"record":{"id":"3f54958cac7c1070","repo":"dgraph-io/dgraph","slug":"unable-to-parse-acls-v","errorCode":null,"errorMessage":"unable to parse ACLs: %v","messagePattern":"unable to parse ACLs: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upgrade/change_v20.03.0.go","lineNumber":50,"sourceCode":"type rule struct {\n\tPredicate  string `json:\"predicate,omitempty\"`\n\tPermission int    `json:\"perm,omitempty\"`\n}\n\ntype rules []rule\n\nfunc upgradeACLRules() error {\n\tdg, cb := x.GetDgraphClient(Upgrade.Conf, true)\n\tdefer cb()\n\n\tdata := make(map[string][]group)\n\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","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/upgrade/change_v20.03.0.go#L32-L68","documentation":"After successfully querying the old ACL data, upgradeACLRules expects a \"rules\" key in the result map holding the groups. If the response parses but lacks that key, the upgrade cannot interpret the ACL state and fails with this message, including the raw data for diagnosis.","triggerScenarios":"The query returned a result whose top-level map has no \"rules\" entry — e.g. the cluster has no dgraph.group.acl data at all, or the response shape differs because the cluster is already on a newer schema version.","commonSituations":"Running the 20.03 upgrade against a cluster that never had ACLs enabled; running it twice (second run finds no old-format data); running against a newer cluster where ACLs were stored in the new format already.","solutions":["Confirm the cluster actually used the v20.03.0 ACL format (dgraph.group.acl predicate) before running the upgrade","If ACLs were never enabled or were already migrated, skip this upgrade step — nothing to do","Inspect the printed data map to see what the query actually returned","Re-run against a backup/snapshot of the pre-upgrade cluster if the old ACLs must be recovered"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// pre-check whether old ACL data exists at all\nout, _ := exec.Command(\"curl\", \"-s\", \"alpha:8080/query\", \"-d\", `{q(func: has(dgraph.group.acl)){dgraph.group.acl}}`).Output()\nif !strings.Contains(string(out), \"dgraph.group.acl\") {\n    fmt.Println(\"no old ACLs; skipping upgrade step\")\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := upgradeACLRules(); err != nil {\n    if strings.Contains(err.Error(), \"unable to parse ACLs\") {\n        // likely no old-format data or already migrated; treat as no-op\n        return nil\n    }\n    return err\n}","preventionTips":["Only run the v20.03 upgrade on clusters that actually used the old ACL format","Never run the same migration twice on the same cluster","Log the raw query result before parsing to ease diagnosis"],"tags":["upgrade","acl","parsing","dgraph"],"backgroundTag":"acl-migration-query-failure","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}