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

  1. Remove @reverse from the scalar predicate.
  2. If reverse traversal is genuinely needed, model the relationship as a uid edge instead of a scalar value.
  3. Only apply @reverse to predicates declared as uid (e.g. 'author: [uid] @reverse').
  4. 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

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


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