{"record":{"id":"cd1e7a8dc7fdb5ae","repo":"dgraph-io/dgraph","slug":"schema-change-not-allowed-from-scalar-to-uid-or-vi","errorCode":null,"errorMessage":"Schema change not allowed from scalar to uid or vice versa while there is data for pred: %s","messagePattern":"Schema change not allowed from scalar to uid or vice versa while there is data for pred: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"worker/mutation.go","lineNumber":452,"sourceCode":"\tcase t.IsScalar() && (t.Enum() == pb.Posting_PASSWORD || s.ValueType == pb.Posting_PASSWORD):\n\t\t// can't change password -> x, x -> password\n\t\tif t.Enum() != s.ValueType {\n\t\t\treturn errors.Errorf(\"Schema change not allowed from %s to %s\",\n\t\t\t\tt.Enum(), typ.Enum())\n\t\t}\n\n\tcase t.IsScalar() == typ.IsScalar():\n\t\t// If old type was list and new type is non-list, we don't allow it until user\n\t\t// has data.\n\t\tif schema.State().IsList(s.Predicate) && !s.List && hasEdges(s.Predicate, math.MaxUint64) {\n\t\t\treturn errors.Errorf(\"Schema change not allowed from [%s] => %s without\"+\n\t\t\t\t\" deleting pred: %s\", t.Name(), typ.Name(), x.ParseAttr(s.Predicate))\n\t\t}\n\n\tdefault:\n\t\t// uid => scalar or scalar => uid. Check that there shouldn't be any data.\n\t\tif hasEdges(s.Predicate, math.MaxUint64) {\n\t\t\treturn errors.Errorf(\"Schema change not allowed from scalar to uid or vice versa\"+\n\t\t\t\t\" while there is data for pred: %s\", x.ParseAttr(s.Predicate))\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateSchemaForUnique(prevSchema pb.SchemaUpdate, currentSchema *pb.SchemaUpdate) error {\n\tvalidTokenizer := func(tokenizers []string) bool {\n\t\tfor _, value := range tokenizers {\n\t\t\tif value == \"hash\" || value == \"exact\" || value == \"int\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\tif prevSchema.Predicate != \"\" {\n\t\tif prevSchema.Upsert && !currentSchema.Upsert {\n\t\t\treturn errors.Errorf(\"could not drop @upsert from [%v] predicate when @unique directive specified\",","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/worker/mutation.go#L434-L470","documentation":"Changing a predicate between uid (edge) and scalar types is only permitted when the predicate holds no data, since the storage layout is completely different. checkSchema's default branch rejects the change when hasEdges(predicate, math.MaxUint64) reports existing data.","triggerScenarios":"Altering 'owner: uid' to 'owner: string' (or vice versa) while any node still has an owner edge; commonly hit when refactoring whether a reference is stored as a uid or an external scalar id.","commonSituations":"Model refactors from node references to external IDs (or back); schema import from another database assuming free type changes; cleanup scripts run against a live database with residual data.","solutions":["Delete all data for the predicate (drop all edges) before altering the type.","Drop the predicate entirely and re-add with the new type, repopulating data afterwards.","Introduce a new predicate with the desired type and migrate values via queries/upsert, then drop the old one.","Check emptiness first with a query like 'pred(count)' or has(pred) before submitting the alteration."],"exampleFix":"// before\nALTER: owner: string .   // owner is uid and has data\n// after\n// delete all owner edges (or drop pred owner), then:\nALTER: owner: string .","handlingStrategy":"validation","validationCode":"func validateUidScalarSwap(pred string, d *dgo.Dgraph) error {\n    resp, err := d.NewReadOnlyTxn().Query(context.Background(),\n        fmt.Sprintf(\"{ q(func: has(%s), first: 1) { uid } }\", pred))\n    if err != nil {\n        return err\n    }\n    if len(resp.Json) > len(\"{}\") {\n        return fmt.Errorf(\"pred %s has data; uid<->scalar change forbidden\", pred)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"err := dgraph.Alter(ctx, op)\nif err != nil && strings.Contains(err.Error(), \"scalar to uid or vice versa\") {\n    // drop pred (or delete all edges), then apply new type\n}","preventionTips":["Treat uid<->scalar as a data migration, not a schema edit","Drop and recreate the predicate when changing storage kind","Run emptiness checks in CI schema-migration scripts"],"tags":["dgraph","schema","uid","type-change"],"backgroundTag":"schema-type-change-not-allowed","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}