{"record":{"id":"b7044fb2424fa92c","repo":"dgraph-io/dgraph","slug":"vars-can-be-assigned-only-when-grouped-by-uid-attr","errorCode":null,"errorMessage":"Vars can be assigned only when grouped by UID attribute","messagePattern":"Vars can be assigned only when grouped by UID attribute","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"query/groupby.go","lineNumber":348,"sourceCode":"\t\t\t}\n\t\t}\n\t\tif child.Params.Var == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tchVar := child.Params.Var\n\n\t\ttempMap := types.NewShardedMap()\n\t\tfor _, grp := range res.group {\n\t\t\tif len(grp.keys) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(grp.keys) > 1 {\n\t\t\t\treturn errors.Errorf(\"Expected one UID for var in groupby but got: %d\", len(grp.keys))\n\t\t\t}\n\t\t\tuidVal := grp.keys[0].key.Value\n\t\t\tuid, ok := uidVal.(uint64)\n\t\t\tif !ok {\n\t\t\t\treturn errors.Errorf(\"Vars can be assigned only when grouped by UID attribute\")\n\t\t\t}\n\t\t\t// grp.aggregates could be empty if schema conversion failed during aggregation\n\t\t\tif len(grp.aggregates) > 0 {\n\t\t\t\ttempMap.Set(uid, grp.aggregates[len(grp.aggregates)-1].key)\n\t\t\t}\n\t\t}\n\t\tdoneVars[chVar] = varValue{\n\t\t\tVals: tempMap,\n\t\t\tpath: append(path, pathNode),\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (sg *SubGraph) processGroupBy(doneVars map[string]varValue, path []*SubGraph) error {\n\tfor _, ul := range sg.uidMatrix {\n\t\t// We need to process groupby for each list as grouping needs to happen for each path of the\n\t\t// tree.","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/query/groupby.go#L330-L366","documentation":"When filling a groupby-exported variable, fillGroupedVars type-asserts the single group key value to uint64 (a UID). If the grouped attribute is not a UID (e.g. a string or int predicate), the variable cannot be keyed by UID, so the query is rejected.","triggerScenarios":"`AS` variable export inside groupby where the grouped key is a non-UID attribute (e.g. groupby(name) with total as sum(x)), so grp.keys[0].key.Value is not uint64.","commonSituations":"Grouping on a string predicate while exporting an aggregate variable; assuming variables work with any groupby attribute; copying a uid-grouped template and swapping the attribute.","solutions":["Change the groupby attribute to uid (or another UID-valued predicate) so the exported variable can be keyed by UID","Remove the AS export and use plain aggregation inside groupby without reusing the variable elsewhere","Compute the aggregate with a separate @groupby/sum query structure that does not bind variables"],"exampleFix":"// before\n{\n  users(func: type(User)) {\n    groupby(name) {\n      total as count(posts)\n    }\n  }\n}\n// after\n{\n  users(func: type(User)) {\n    groupby(uid) {\n      total as count(posts)\n    }\n  }\n}","handlingStrategy":"validation","validationCode":"// Ensure the groupby attribute is UID-valued when exporting a variable:\nfunction validateGroupbyAttrIsUid(attr, exportsVar) {\n  if (exportsVar && attr !== 'uid') {\n    throw new Error('Vars can only be exported when grouped by uid');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const res = await dgraph.newTxn().query(query);\n} catch (e) {\n  if (String(e).includes('grouped by UID attribute')) {\n    // rewrite query to groupby(uid) or drop the AS export\n  } else throw e;\n}","preventionTips":["Only export groupby variables when grouping on uid","Do not swap the groupby attribute of a template that exports variables without checking UID-ness","Prefer computing aggregates client-side when grouping on non-UID attributes"],"tags":["dgraph","graphql-plus","groupby","query-validation"],"backgroundTag":"groupby-variable-misuse","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}