{"record":{"id":"be19f15b18c1661d","repo":"dgraph-io/dgraph","slug":"couldn-t-evaluate-normalize-directive-too-many","errorCode":null,"errorMessage":"Couldn't evaluate @normalize directive - too many results","messagePattern":"Couldn't evaluate @normalize directive - too many results","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"query/outputnode.go","lineNumber":901,"sourceCode":"\treturn nn\n}\n\nfunc (enc *encoder) merge(parent, child []fastJsonNode) ([]fastJsonNode, error) {\n\tif len(parent) == 0 {\n\t\treturn child, nil\n\t}\n\n\t// Here we merge two slices of maps.\n\tmergedList := make([]fastJsonNode, 0)\n\tcnt := 0\n\tfor _, pa := range parent {\n\t\tfor _, ca := range child {\n\t\t\tpaCopy, paNodeCount := enc.copyFastJsonList(pa)\n\t\t\tcaCopy, caNodeCount := enc.copyFastJsonList(ca)\n\n\t\t\tcnt += paNodeCount + caNodeCount\n\t\t\tif cnt > x.Config.LimitNormalizeNode {\n\t\t\t\treturn nil, errors.Errorf(\n\t\t\t\t\t\"Couldn't evaluate @normalize directive - too many results\")\n\t\t\t}\n\n\t\t\tif paCopy == nil {\n\t\t\t\tpaCopy = caCopy\n\t\t\t} else {\n\t\t\t\ttemp := paCopy\n\t\t\t\tfor temp.next != nil {\n\t\t\t\t\ttemp = temp.next\n\t\t\t\t}\n\t\t\t\ttemp.next = caCopy\n\t\t\t}\n\t\t\tmergedList = append(mergedList, paCopy)\n\t\t}\n\t}\n\treturn mergedList, nil\n}\n","sourceCodeStart":883,"sourceCodeEnd":919,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/query/outputnode.go#L883-L919","documentation":"The @normalize directive flattens and merges result branches into edge-list form. During merge, Dgraph caps total materialized nodes with the LimitNormalizeNode config; exceeding it aborts the query since the response would be unbounded.","triggerScenarios":"A @normalize query whose combined parent+child lists exceed x.Config.LimitNormalizeNode (default 1e6) — e.g. a broad root match with a nested reverse edge expanded for every node, cross-producting results.","commonSituations":"Queries on large graphs without filters; @normalize combined with recurse or expand(_all_); instances with default LIMIT_NORMALIZE_NODE config on datasets larger than intended.","solutions":["Add tighter filters/pagination (first, offset) to the root or nested blocks to reduce result count","Raise the limit via --limit and/or the LIMIT_NORMALIZE_NODE option (same name) in Greater Limit options if the result size is genuinely intended","Restructure the query: split into smaller queries without @normalize, or aggregate instead of returning every edge","Filter child edges (e.g. @filter(has(...))) so fewer branches are merged per parent"],"exampleFix":"// before\n{\n  q(func: type(Person)) @normalize {\n    name\n    friend { friendName: name }\n  }\n}\n// after\n{\n  q(func: type(Person), first: 1000) @normalize {\n    name\n    friend (first: 10) { friendName: name }\n  }\n}","handlingStrategy":"try-catch","validationCode":"// estimate result size first with a count query\nconst [{ count }] = await dgraph.query('{ c(func: type(Person)) { count(uid) } }');\nif (count * avgFanout > 1_000_000) throw new Error('normalize result would exceed limit; paginate');","typeGuard":null,"tryCatchPattern":"try {\n  return await dgraph.query(q); // q uses @normalize\n} catch (e) {\n  if (String(e).includes('too many results') && /normalize/.test(q)) {\n    return await dgraph.query(paginate(q)); // retry with first/offset\n  }\n  throw e;\n}","preventionTips":["Always pair @normalize with first/offset pagination on the root block","Cap nested edge expansion with (first: N) inside normalized blocks","Tune LimitNormalizeNode only after estimating true result size","Prefer aggregations over full edge lists for analytics queries"],"tags":["dgraph","normalize","limit","query"],"backgroundTag":"result-size-limit-exceeded","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}