dgraph-io/dgraph · error

Index tokenizer is mandatory for: [%s] when specifying @upse

Error message

Index tokenizer is mandatory for: [%s] when specifying @upsert directive

What it means

The @upsert directive relies on an index to detect conflicting writes, so Dgraph requires that a predicate marked @upsert also carry at least one index tokenizer (or be @unique). checkSchema rejects Upsert=true with no tokenizers and not unique.

Source

Thrown at worker/mutation.go:414

	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)
	if err != nil {
		// No schema previously defined, so no need to do checks about schema conversions.
		return nil
	}

	// schema was defined already

View on GitHub (pinned to 759e242be6)

Solutions

  1. Combine @upsert with @index and a tokenizer, e.g. 'email: string @index(hash) @upsert'.
  2. Alternatively use @unique (which implies an index requirement handled separately), e.g. 'email: string @index(hash) @upsert @unique'.
  3. If upsert behavior is not needed, drop @upsert.
  4. Set s.Tokenizer in code whenever s.Upsert is set true.

Example fix

// before
email: string @upsert .
// after
email: string @index(hash) @upsert .
Defensive patterns

Strategy: validation

Validate before calling

func validateUpsert(s *pb.SchemaUpdate) error {
    if s.Upsert && len(s.Tokenizer) == 0 && !s.Unique {
        return fmt.Errorf("predicate %s: @upsert requires an index tokenizer or @unique", s.Predicate)
    }
    return nil
}

Type guard

func upsertIsValid(s *pb.SchemaUpdate) bool {
    return !s.Upsert || len(s.Tokenizer) > 0 || s.Unique
}

Try / catch

err := dgraph.Alter(ctx, op)
if err != nil && strings.Contains(err.Error(), "Index tokenizer is mandatory") {
    // add @index(tokenizer) alongside @upsert, then retry
}

Prevention

When it happens

Trigger: Altering a predicate to add @upsert without @index, e.g. 'email: string @upsert', or setting s.Upsert = true on a pb.SchemaUpdate while Tokenizer is empty and Unique is false.

Common situations: Adding upsert semantics for deduplication but forgetting that it needs an index; migrating schemas where indexes were dropped while @upsert remained; tutorials showing @upsert without showing the accompanying @index.

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/cec0614225d50814. Report an issue: GitHub.