dgraph-io/dgraph · error

Only aggregated variables allowed within empty block.

Error message

Only aggregated variables allowed within empty block.

What it means

When a sub-block of a query yields no UIDs, Dgraph still tries to surface its aggregated variables. If the aggregation block is empty AND it has no tracked input variables (NeedsVar empty), there is no aggregation value to encode — the query only contained non-aggregation content in an empty block, which is invalid.

Source

Thrown at query/outputnode.go:1045

			}
		}
		for _, it := range grp.aggregates {
			if err := enc.AddValue(uc, enc.idForAttr(it.attr), it.key); err != nil {
				return err
			}
		}
		enc.AddListChild(g, uc)
	}
	enc.AddListChild(fj, g)
	return nil
}

func (sg *SubGraph) addAggregations(enc *encoder, fj fastJsonNode) error {
	for _, child := range sg.Children {
		aggVal, ok := child.Params.UidToVal.Get(0)
		if !ok {
			if len(child.Params.NeedsVar) == 0 {
				return errors.Errorf("Only aggregated variables allowed within empty block.")
			}
			// the aggregation didn't happen, most likely was called with unset vars.
			// See: query.go:fillVars
			// In this case we do nothing. The aggregate value in response will be returned as NULL.
		}
		if child.Params.Normalize && child.Params.Alias == "" {
			continue
		}
		fieldName := child.aggWithVarFieldName()
		n1 := enc.newNode(enc.idForAttr(sg.Params.Alias))
		if err := enc.AddValue(n1, enc.idForAttr(fieldName), aggVal); err != nil {
			return err
		}
		enc.AddListChild(fj, n1)
	}
	if enc.IsEmpty(fj) {
		enc.AddListChild(fj, enc.newNode(enc.idForAttr(sg.Params.Alias)))
	}

View on GitHub (pinned to 759e242be6)

Solutions

  1. Fix the variable name/declaration so the aggregation references a var actually produced by an earlier block
  2. Loosen or correct the filter that empties the block so it matches nodes
  3. Ensure aggregate(math(...)) blocks reference grouped/source variables (aggregations require an input var)
  4. Check query.go fillVars path: confirm the upstream block that should set the var actually executes before this block

Example fix

// before
{
  var(func: has(score)) @filter(gt(score, 100)) {
    s as score
  }
  q(func: uid(0)) {
    total as sum(val(s))
  }
}
// after: guard for empty result or fix filter
{
  var(func: has(score)) {
    s as score
  }
  q(func: uid(0)) {
    total as sum(val(s))
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// verify the source block matches before running the aggregation query
const probe = await dgraph.query('{ p(func: has(score)) @filter(gt(score, 100)) { uid } }');
if (!probe.p.length) throw new Error('aggregation source block is empty; fix filter/vars first');

Try / catch

try {
  return await dgraph.query(q);
} catch (e) {
  if (String(e).includes('Only aggregated variables allowed within empty block')) {
    // treat as empty aggregate: return 0/null, or retry with corrected variable names
  }
  throw e;
}

Prevention

When it happens

Trigger: A query block that matches zero nodes containing a math/aggregate variable whose source variables were never set or matched nothing, so UidToVal.Get(0) fails and child.Params.NeedsVar is empty during addAggregations (called from processNodeUids).

Common situations: Aggregate inside a block filtered to zero results where the var was declared with the wrong name or the filter excludes everything; typos in variable names so fillVars never populates them; @groupby/math blocks under empty parent UIDs.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/d5f350b0c148753b. Report an issue: GitHub.