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
- Ensure the predicate being aggregated/read is a scalar type in the schema
- Use the uid-typed predicate only in edge traversal, not in value conversion paths
- 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
- Verify schema types before writing value-conversion code
- Only pass scalar predicates (string, int, float, bool, datetime) to aggregation
- Guard against schema drift between environments
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
- provided value is not a scalar, can't convert it to string
- error interpreting appropriate type for %v
- while fetching types
- Value of type: %s isn't sortable
- Arguments of different type can not be compared.
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/80f7e3f290101b06.
Report an issue: GitHub.