dgraph-io/dgraph · error
Cannot reverse for non-uid type on predicate %s
Error message
Cannot reverse for non-uid type on predicate %s
What it means
The @reverse directive (storing uid -> owning-node reverse edges) is only meaningful for predicates whose values are uids. checkSchema rejects updates where the value type is not uid but Directive is SchemaUpdate_REVERSE, e.g. putting @reverse on a string or int predicate.
Source
Thrown at worker/mutation.go:408
x.ParseAttr(s.Predicate))
}
if s.Directive == pb.SchemaUpdate_INDEX && !schema.HasTokenizerOrVectorIndexSpec(s) {
return errors.Errorf("Tokenizer must be specified while indexing a predicate: %+v", s)
}
if schema.HasTokenizerOrVectorIndexSpec(s) && s.Directive != pb.SchemaUpdate_INDEX {
return errors.Errorf("Directive must be SchemaUpdate_INDEX when a tokenizer is specified")
}
typ := types.TypeID(s.ValueType)
if typ == types.UidID && s.Directive == pb.SchemaUpdate_INDEX {
// index on uid type
return errors.Errorf("Index not allowed on predicate of type uid on predicate %s",
x.ParseAttr(s.Predicate))
} else if typ != types.UidID && s.Directive == pb.SchemaUpdate_REVERSE {
// reverse on non-uid type
return errors.Errorf("Cannot reverse for non-uid type on predicate %s",
x.ParseAttr(s.Predicate))
}
// If schema update has upsert directive, it should have index directive.
if s.Upsert && len(s.Tokenizer) == 0 && !s.Unique {
return errors.Errorf("Index tokenizer is mandatory for: [%s] when specifying @upsert directive",
x.ParseAttr(s.Predicate))
}
if s.Unique {
ctx := context.WithValue(context.Background(), schema.IsWrite, false)
prevSchema, _ := schema.State().Get(ctx, s.Predicate)
if err := validateSchemaForUnique(prevSchema, s); err != nil {
return err
}
}
t, err := schema.State().TypeOf(s.Predicate)View on GitHub (pinned to 759e242be6)
Solutions
- Remove @reverse from the scalar predicate.
- If reverse traversal is genuinely needed, model the relationship as a uid edge instead of a scalar value.
- Only apply @reverse to predicates declared as uid (e.g. 'author: [uid] @reverse').
- Add a pre-flight check that Directive==REVERSE implies ValueType==UID in schema-generation code.
Example fix
// before age: int @reverse . // after age: int .
Defensive patterns
Strategy: validation
Validate before calling
func validateReverseOnUid(s *pb.SchemaUpdate) error {
if types.TypeID(s.ValueType) != types.UidID && s.Directive == pb.SchemaUpdate_REVERSE {
return fmt.Errorf("predicate %s: @reverse only valid on uid", s.Predicate)
}
return nil
} Type guard
func canReverse(s *pb.SchemaUpdate) bool {
return types.TypeID(s.ValueType) == types.UidID && s.Directive == pb.SchemaUpdate_REVERSE
} Try / catch
err := dgraph.Alter(ctx, op)
if err != nil && strings.Contains(err.Error(), "Cannot reverse for non-uid type") {
// drop @reverse from the predicate
} Prevention
- Only add @reverse to uid-typed predicates
- Lint schema files: flag @reverse on scalar types
- Model reverse lookups on scalars with a uid edge instead
When it happens
Trigger: Altering a scalar predicate with @reverse, e.g. 'age: int @reverse', or building pb.SchemaUpdate with ValueType != Posting_UID and Directive: pb.SchemaUpdate_REVERSE.
Common situations: Bulk-adding @reverse to every predicate in a schema; expecting reverse traversal on scalar fields; IDE autocomplete or tooling that appends directives indiscriminately.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Index not allowed on predicate of type uid on predicate %s
- Schema change not allowed from scalar to uid or vice versa w
- illegal rune found "%c", expecting {
- JSON map is followed by illegal rune "%c"
- Malformed JSON
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/eba07a456dbf8705.
Report an issue: GitHub.