{"record":{"id":"1222435c490f599a","repo":"dgraph-io/dgraph","slug":"field-in-type-definition-cannot-have-a-directive","errorCode":null,"errorMessage":"Field in type definition cannot have a directive","messagePattern":"Field in type definition cannot have a directive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"worker/mutation.go","lineNumber":831,"sourceCode":"\t}\n\n\treturn nil\n}\n\n// typeSanityCheck performs basic sanity checks on the given type update.\nfunc typeSanityCheck(t *pb.TypeUpdate) error {\n\tfor _, field := range t.Fields {\n\t\tif x.ParseAttr(field.Predicate) == \"\" {\n\t\t\treturn errors.Errorf(\"Field in type definition must have a name\")\n\t\t}\n\n\t\tif field.ValueType == pb.Posting_OBJECT && field.ObjectTypeName == \"\" {\n\t\t\treturn errors.Errorf(\n\t\t\t\t\"Field with value type OBJECT must specify the name of the object type\")\n\t\t}\n\n\t\tif field.Directive != pb.SchemaUpdate_NONE {\n\t\t\treturn errors.Errorf(\"Field in type definition cannot have a directive\")\n\t\t}\n\n\t\tif len(field.Tokenizer) > 0 {\n\t\t\treturn errors.Errorf(\"Field in type definition cannot have tokenizers\")\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// CommitOverNetwork makes a proxy call to Zero to commit or abort a transaction.\nfunc CommitOverNetwork(ctx context.Context, tc *api.TxnContext) (uint64, error) {\n\tctx, span := otel.Tracer(\"\").Start(ctx, \"worker.CommitOverNetwork\")\n\tdefer span.End()\n\n\tclientDiscard := false\n\tif tc.Aborted {\n\t\t// The client called Discard","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/worker/mutation.go#L813-L849","documentation":"typeSanityCheck rejects any type-definition field that carries a directive (e.g. @index, @upsert, @reverse, @lang). Directives belong to predicate (schema) definitions, not to type definitions, so a type block that re-declares a field with a directive set on pb.SchemaUpdate fails this check. This keeps the type system purely about grouping predicates under a named type.","triggerScenarios":"Sending an alter/schema update whose `type` block fields have Directive set to anything other than pb.SchemaUpdate_NONE — typically when a generator copies predicate schema entries (with their directives) into type definitions.","commonSituations":"Schema migration scripts that reuse the same struct for predicates and type fields, accidentally carrying @index/@upsert directives into type blocks; hand-written DQL where directives are placed inside a `type X { }` block instead of at the predicate line.","solutions":["Remove all directives from the type block; keep them only on the corresponding predicate declarations in the schema section","e.g. write `type Person { name address }` and declare `name: string @index(exact) .` separately at the predicate level","Fix any code generator that populates field.Directive when building pb.SchemaUpdate type definitions"],"exampleFix":"// before\ntype Person {\n  name @index(exact)\n}\n// after\nname: string @index(exact) .\ntype Person {\n  name\n}","handlingStrategy":"validation","validationCode":"for _, f := range typeDef.Fields {\n    if f.Directive != pb.SchemaUpdate_NONE {\n        return fmt.Errorf(\"field %q: directives belong on predicates, not type defs\", f.Predicate)\n    }\n}","typeGuard":"func fieldHasNoDirective(f *pb.SchemaUpdate) bool {\n    return f.Directive == pb.SchemaUpdate_NONE\n}","tryCatchPattern":null,"preventionTips":["Keep directives (@index, @upsert, @reverse) only in predicate schema lines","Use separate structs/entries for predicate schema and type definitions","Copy-paste check: never reuse predicate pb.SchemaUpdate objects as type fields"],"tags":["dgraph","schema","type-definition","directives"],"backgroundTag":"schema-validation-failed","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}