{"record":{"id":"cec0614225d50814","repo":"dgraph-io/dgraph","slug":"index-tokenizer-is-mandatory-for-s-when-specif","errorCode":null,"errorMessage":"Index tokenizer is mandatory for: [%s] when specifying @upsert directive","messagePattern":"Index tokenizer is mandatory for: \\[(.+?)\\] when specifying @upsert directive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"worker/mutation.go","lineNumber":414,"sourceCode":"\n\tif schema.HasTokenizerOrVectorIndexSpec(s) && s.Directive != pb.SchemaUpdate_INDEX {\n\t\treturn errors.Errorf(\"Directive must be SchemaUpdate_INDEX when a tokenizer is specified\")\n\t}\n\n\ttyp := types.TypeID(s.ValueType)\n\tif typ == types.UidID && s.Directive == pb.SchemaUpdate_INDEX {\n\t\t// index on uid type\n\t\treturn errors.Errorf(\"Index not allowed on predicate of type uid on predicate %s\",\n\t\t\tx.ParseAttr(s.Predicate))\n\t} else if typ != types.UidID && s.Directive == pb.SchemaUpdate_REVERSE {\n\t\t// reverse on non-uid type\n\t\treturn errors.Errorf(\"Cannot reverse for non-uid type on predicate %s\",\n\t\t\tx.ParseAttr(s.Predicate))\n\t}\n\n\t// If schema update has upsert directive, it should have index directive.\n\tif s.Upsert && len(s.Tokenizer) == 0 && !s.Unique {\n\t\treturn errors.Errorf(\"Index tokenizer is mandatory for: [%s] when specifying @upsert directive\",\n\t\t\tx.ParseAttr(s.Predicate))\n\t}\n\n\tif s.Unique {\n\t\tctx := context.WithValue(context.Background(), schema.IsWrite, false)\n\t\tprevSchema, _ := schema.State().Get(ctx, s.Predicate)\n\t\tif err := validateSchemaForUnique(prevSchema, s); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tt, err := schema.State().TypeOf(s.Predicate)\n\tif err != nil {\n\t\t// No schema previously defined, so no need to do checks about schema conversions.\n\t\treturn nil\n\t}\n\n\t// schema was defined already","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/worker/mutation.go#L396-L432","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nemail: string @upsert .\n// after\nemail: string @index(hash) @upsert .","handlingStrategy":"validation","validationCode":"func validateUpsert(s *pb.SchemaUpdate) error {\n    if s.Upsert && len(s.Tokenizer) == 0 && !s.Unique {\n        return fmt.Errorf(\"predicate %s: @upsert requires an index tokenizer or @unique\", s.Predicate)\n    }\n    return nil\n}","typeGuard":"func upsertIsValid(s *pb.SchemaUpdate) bool {\n    return !s.Upsert || len(s.Tokenizer) > 0 || s.Unique\n}","tryCatchPattern":"err := dgraph.Alter(ctx, op)\nif err != nil && strings.Contains(err.Error(), \"Index tokenizer is mandatory\") {\n    // add @index(tokenizer) alongside @upsert, then retry\n}","preventionTips":["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"],"tags":["dgraph","schema","upsert","indexing"],"backgroundTag":"schema-validation-failed","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}