dgraph-io/dgraph · error
Node with %q cant have filter, please place the filter on th
Error message
Node with %q cant have filter, please place the filter on the upper level
What it means
A node carrying an aggregator or password-verifier function must not also carry an @filter directive. An embedded filter under an aggregate is ambiguous — e.g. `min(...) @filter(...)` inside another filter — so treeCopy rejects it and asks that the filter be placed at the parent/upper level instead.
Source
Thrown at query/query.go:629
if gchild.MathExp != nil {
mathExp := &mathTree{}
if err := mathCopy(mathExp, gchild.MathExp); err != nil {
return err
}
dst.MathExp = mathExp
}
if gchild.Func != nil &&
(gchild.Func.IsAggregator() || gchild.Func.IsPasswordVerifier()) {
if len(gchild.Children) != 0 {
return errors.Errorf("Node with %q cant have child attr", gchild.Func.Name)
}
// embedded filter will cause ambiguous output like following,
// director.film @filter(gt(initial_release_date, "2016")) {
// min(initial_release_date @filter(gt(initial_release_date, "1986"))
// }
if gchild.Filter != nil {
return errors.Errorf(
"Node with %q cant have filter, please place the filter on the upper level",
gchild.Func.Name)
}
if gchild.Func.Attr == "uid" {
return errors.Errorf(`Argument cannot be "uid"`)
}
dst.createSrcFunction(gchild.Func)
}
if gchild.Filter != nil {
dstf := &SubGraph{}
if err := filterCopy(dstf, gchild.Filter); err != nil {
return err
}
dst.Filters = append(dst.Filters, dstf)
}
if gchild.FacetsFilter != nil {View on GitHub (pinned to 759e242be6)
Solutions
- Move the @filter to the parent level so filtering happens on the set before aggregation
- Filter the scalar predicate inside the parent's filter: e.g. `friend @filter(gt(score, 3)) { min(score) }`
- If filtering aggregated results is needed, store the aggregate in a variable and apply it in a func/filter at the root
Example fix
// before
{
me(func: uid(0x1)) {
friend {
min(initial_release_date) @filter(gt(initial_release_date, "1986"))
}
}
}
// after
{
me(func: uid(0x1)) {
friend @filter(gt(initial_release_date, "1986")) {
min(initial_release_date)
}
}
} Defensive patterns
Strategy: validation
Validate before calling
function assertNoFilterOnAggregate(node) {
if (/^(min|max|sum|avg|count)\s*\(/.test(node.field) && node.filter) {
throw new Error('Move the @filter from the aggregated node to the parent level');
}
} Type guard
const isAggregateWithFilter = (n) => Boolean(n.func && n.func.isAggregator && n.func.isAggregator() && n.filter);
Try / catch
try {
await txn.query(q);
} catch (e) {
if (e.message.includes('cant have filter')) {
throw new Error('Relocate @filter to the parent level of the aggregate');
}
throw e;
} Prevention
- Attach @filter to the parent edge, never to an aggregate node
- To filter before aggregation, filter the scalar predicate at the parent
- To filter after aggregation, use aggregator variables in a root func/filter
When it happens
Trigger: Query like `director @filter(...)` where the child is `min(initial_release_date) @filter(gt(initial_release_date, "1986"))` — i.e. a filter attached to the aggregated node itself.
Common situations: Attempting to filter values before aggregation in a single expression; copy-paste of filters across levels; older queries retargeted after adding an aggregate.
Related errors
- Only aggregated variables allowed within empty block.
- Node with %q cant have child attr
- Invalid variable aggregation. Check the levels.
- Error in applying aggregation %s
- count(predicate) cannot be used to search for negative count
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/2ee380aff4248f32.
Report an issue: GitHub.