dgraph-io/dgraph · error

Leaf predicate:'%v' must be a scalar.

Error message

Leaf predicate:'%v' must be a scalar.

What it means

convertWithBestEffort converts an internal task value to a typed value using the schema type. If the value's type ID is not a scalar (i.e. it is a UID/list-of-UID), the conversion cannot proceed and this error is thrown, because only scalar predicates can be converted to a plain value here.

Source

Thrown at query/query.go:429

			return true
		}
		return false
	}
	return true
}

func isEmptyIneqFnWithVar(sg *SubGraph) bool {
	return sg.SrcFunc != nil && isInequalityFn(sg.SrcFunc.Name) && len(sg.SrcFunc.Args) == 0 &&
		len(sg.Params.NeedsVar) > 0
}

// convert from task.Val to types.Value, based on schema appropriate type
// is already set in api.Value
func convertWithBestEffort(tv *pb.TaskValue, attr string) (types.Val, error) {
	// value would be in binary format with appropriate type
	tid := types.TypeID(tv.ValType)
	if !tid.IsScalar() {
		return types.Val{}, errors.Errorf("Leaf predicate:'%v' must be a scalar.", attr)
	}

	// creates appropriate type from binary format
	sv, err := types.Convert(types.Val{Tid: types.BinaryID, Value: tv.Val}, tid)
	if err != nil {
		// This can happen when a mutation ingests corrupt data into the database.
		return types.Val{}, errors.Wrapf(err, "error interpreting appropriate type for %v", attr)
	}
	return sv, nil
}

func mathCopy(dst *mathTree, src *dql.MathTree) error {
	// Either we'll have an operation specified, or the function specified.
	dst.Const = src.Const
	dst.Fn = src.Fn
	dst.Val = src.Val
	dst.Var = src.Var

View on GitHub (pinned to 759e242be6)

Solutions

  1. Ensure the predicate being aggregated/read is a scalar type in the schema
  2. Use the uid-typed predicate only in edge traversal, not in value conversion paths
  3. Fix corrupt/inconsistent schema so the value type matches usage

Example fix

// before
val, err := convertWithBestEffort(tv, "friend") // friend is uid predicate
// after
val, err := convertWithBestEffort(tv, "name") // scalar predicate, e.g. string
Defensive patterns

Strategy: type-guard

Validate before calling

// Go: check schema type is scalar before treating value as scalar leaf
var sch types.Schema
if err := client.CheckSchema(ctx, pred, &sch); err == nil && !types.TypeID(sch.Type).IsScalar() {
    return fmt.Errorf("predicate %s is not scalar; cannot aggregate/convert", pred)
}

Type guard

func isScalarValue(tv *pb.TaskValue) bool {
    return types.TypeID(tv.ValType).IsScalar()
}

Try / catch

val, err := convertWithBestEffort(tv, attr)
if err != nil && strings.Contains(err.Error(), "must be a scalar") {
    // treat as edge/uid predicate: handle as UID list instead
    return handleUidPredicate(tv, attr)
}

Prevention

When it happens

Trigger: Calling convertWithBestEffort with a TaskValue whose ValType is a UID type — e.g. preTraverse reading a node-predicate value as if it were scalar, aggregateGroup aggregating a non-scalar predicate, rdfForValueList on uid lists.

Common situations: Aggregating or traversing over a predicate that stores UIDs (edges) but treating it as a leaf/scalar in math/aggregation code; schema drift where a predicate changed from scalar to uid.

Related errors


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