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 alreadyView on GitHub (pinned to 759e242be6)
Solutions
- Combine @upsert with @index and a tokenizer, e.g. 'email: string @index(hash) @upsert'.
- Alternatively use @unique (which implies an index requirement handled separately), e.g. 'email: string @index(hash) @upsert @unique'.
- If upsert behavior is not needed, drop @upsert.
- 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
- Always write @upsert together with @index(tokenizer)
- Remember @unique does not remove the tokenizer requirement in code paths
- Add upsert directives only via helper functions that set tokenizer too
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
- Indexing not allowed on predicate %s of type %s
- Require type of tokenizer for pred: %s of type: %s for index
- Tokenizers present without indexing on attr %s
- failed to enrich schema
- Tokenizer must be specified while indexing a predicate: %+v
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/cec0614225d50814.
Report an issue: GitHub.