{"record":{"id":"fecb31582042e0b0","repo":"dgraph-io/dgraph","slug":"directive-must-be-schemaupdate-index-when-a-tokeni","errorCode":null,"errorMessage":"Directive must be SchemaUpdate_INDEX when a tokenizer is specified","messagePattern":"Directive must be SchemaUpdate_INDEX when a tokenizer is specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"worker/mutation.go","lineNumber":398,"sourceCode":"\tif s == nil {\n\t\treturn errors.Errorf(\"Nil schema\")\n\t}\n\n\tif x.ParseAttr(s.Predicate) == \"\" {\n\t\treturn errors.Errorf(\"No predicate specified in schema mutation\")\n\t}\n\n\tif x.IsInternalPredicate(s.Predicate) {\n\t\treturn errors.Errorf(\"Cannot create user-defined predicate with internal name %s\",\n\t\t\tx.ParseAttr(s.Predicate))\n\t}\n\n\tif s.Directive == pb.SchemaUpdate_INDEX && !schema.HasTokenizerOrVectorIndexSpec(s) {\n\t\treturn errors.Errorf(\"Tokenizer must be specified while indexing a predicate: %+v\", s)\n\t}\n\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}","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/worker/mutation.go#L380-L416","documentation":"This is the mirror check of the tokenizer rule: if a schema update specifies tokenizers (or a vector index spec) via HasTokenizerOrVectorIndexSpec, the Directive must be SchemaUpdate_INDEX. Dgraph throws it because tokenizers are meaningless without an index being built, so it treats tokenizer-without-@index as an invalid schema.","triggerScenarios":"Altering a predicate with tokenizers but without @index, e.g. 'name: string @hash' or a pb.SchemaUpdate with Tokenizer: [\"hash\"] and Directive: pb.SchemaUpdate_NONE; typically after someone removed @index but left the tokenizer, or edited the schema programmatically.","commonSituations":"Manual schema edits that delete '@index' but keep the tokenizer in parentheses; code that toggles indexing off by changing only the Directive field while leaving Tokenizer populated; schema merge tools that combine fields from different versions.","solutions":["Add the @index directive back: 'name: string @index(hash)' or set s.Directive = pb.SchemaUpdate_INDEX.","If indexing is not wanted, remove the tokenizer as well, e.g. 'name: string' or s.Tokenizer = nil.","Make Directive and Tokenizer consistent in code by deriving Directive from whether Tokenizer is non-empty.","Validate the schema with checkSchema/TestCheckSchema-style logic in unit tests before shipping the mutation."],"exampleFix":"// before\nname: string @hash .\n// after\nname: string @index(hash) .","handlingStrategy":"validation","validationCode":"func validateTokenizerDirective(s *pb.SchemaUpdate) error {\n    if schema.HasTokenizerOrVectorIndexSpec(s) && s.Directive != pb.SchemaUpdate_INDEX {\n        return fmt.Errorf(\"predicate %s: tokenizer requires @index\", s.Predicate)\n    }\n    return nil\n}","typeGuard":"func tokenizerConsistent(s *pb.SchemaUpdate) bool {\n    has := len(s.Tokenizer) > 0\n    indexed := s.Directive == pb.SchemaUpdate_INDEX\n    return has == indexed\n}","tryCatchPattern":"err := dgraph.Alter(ctx, op)\nif err != nil && strings.Contains(err.Error(), \"Directive must be SchemaUpdate_INDEX\") {\n    // re-set Directive to INDEX or clear Tokenizer, then retry\n}","preventionTips":["Derive Directive from Tokenizer presence in schema builders","Never delete @index while keeping tokenizer arguments","Diff old vs new schema before ALTER"],"tags":["dgraph","schema","indexing","tokenizer"],"backgroundTag":"schema-validation-failed","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}