dgraph-io/dgraph · error

Variable not found in math

Error message

Variable not found in math

What it means

MathTree.subs substitutes query variables (names starting with `$`) in a math expression tree with their concrete values using the provided varMap. This error is thrown when a `$var` referenced in a math() block is not present in the variable map, i.e. the variable was never defined by an earlier query clause. The library cannot evaluate the expression without it, so it fails fast.

Source

Thrown at dql/math.go:351

	if valueStack.empty() {
		// This happens when we have math(). We can either return an error or
		// ignore. Currently, let's just ignore and pretend there is no expression.
		return nil, false, errors.Errorf("Empty () not allowed in math block.")
	}

	if valueStack.size() != 1 {
		return nil, false, errors.Errorf("Expected one item in value stack, but got %d",
			valueStack.size())
	}
	res, err := valueStack.pop()
	return res, false, err
}

func (t *MathTree) subs(vmap varMap) error {
	if strings.HasPrefix(t.Var, "$") {
		va, ok := vmap[t.Var]
		if !ok {
			return errors.Errorf("Variable not found in math")
		}
		var err error
		t.Const, err = parseValue(va)
		if err != nil {
			return err
		}
		t.Var = ""
	}
	for _, i := range t.Child {
		if err := i.subs(vmap); err != nil {
			return err
		}
	}
	return nil
}

// debugString converts mathTree to a string. Good for testing, debugging.
// nolint: unused

View on GitHub (pinned to 759e242be6)

Solutions

  1. Define the variable before the math block, e.g. add a clause like `countOfFriends as count(friend)` so `$countOfFriends` exists in the var map.
  2. Check for case/typo mismatches between the `$name` used in math() and the name emitted by the defining clause.
  3. If the query was split into parts, re-join it so variable-producing clauses execute in the same request.
  4. Wrap the math expression in a guard (e.g. math($var == nil ? 0 : ...)) only if the version supports nil checks; otherwise provide a default value clause.

Example fix

// before
{
  me(func: uid(0x1)) {
    val(score) // $score never defined
    f as math($score * 2)
  }
}
// after
{
  var(func: uid(0x1)) {
    friend {
      score as math(1)
    }
    s as sum(val(score))
  }
  me(func: uid(0x1)) {
    f as math($s * 2)
  }
}
Defensive patterns

Strategy: validation

Validate before calling

func hasVarDef(query, varName string) bool {
    // varName like "score"; look for 'as ...' clause producing it before use
    return strings.Contains(query, varName+" as ") || strings.Contains(query, " as "+varName)
}
// check every $var in math() has a corresponding 'X as ...' definition

Try / catch

err := runQuery(txn, q)
if err != nil && strings.Contains(err.Error(), "Variable not found in math") {
    return fmt.Errorf("query variable used in math() is not defined by any prior clause: %w", err)
}

Prevention

When it happens

Trigger: A DQL query contains `math($someVar ...)` but no prior query node defines `someVar as *someVar` (variable not bound), so vmap lookup for "$someVar" misses.

Common situations: Typos in variable names (case mismatch: `$count` vs `$Count`), restructuring a query and removing the clause that emitted the variable, or running a sub-query standalone that depended on variables from a parent query.

Related errors


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