{"record":{"id":"9500850f77a1904b","repo":"dgraph-io/dgraph","slug":"schema-change-not-allowed-from-s-s-without","errorCode":null,"errorMessage":"Schema change not allowed from [%s] => %s without deleting pred: %s","messagePattern":"Schema change not allowed from \\[(.+?)\\] => (.+?) without deleting pred: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"worker/mutation.go","lineNumber":445,"sourceCode":"\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\n\tswitch {\n\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","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/worker/mutation.go#L427-L463","documentation":"Dgraph does not allow converting a list-type predicate ([T]) back to a single-value predicate (T) while any data exists for that predicate, because existing postings store multiple values per node. checkSchema checks IsList(predicate) && !s.List && hasEdges(...) and rejects the change naming the old and new types.","triggerScenarios":"Altering 'tags: [string] @index(term)' to 'tags: string' while nodes still carry values for tags; the hasEdges check on math.MaxUint64 finds data and rejects it.","commonSituations":"Simplifying a schema after deciding lists are unnecessary; accidental removal of the [] brackets when editing DQL schema; ORM-generated schema drift where the model changed from array to scalar.","solutions":["Delete all data for the predicate first (e.g. upsert/mutation deleting tags for every node, or drop the predicate), then change the type.","Keep the list type and treat single values as one-element lists in queries instead of changing the schema.","Migrate to a new scalar predicate: write collapsed values to a new pred, then drop the old list pred.","Verify with a count query that the predicate has no edges before attempting the alteration."],"exampleFix":"// before\nALTER: tags: string @index(term) .   // tags currently [string] with data\n// after\n// first delete all tags edges, then:\nALTER: tags: string @index(term) .","handlingStrategy":"validation","validationCode":"func validateListCollapse(pred string, wantList bool, d *dgo.Dgraph) error {\n    if schema.State().IsList(pred) && !wantList {\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 still has data; delete before list->scalar\", pred)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"err := dgraph.Alter(ctx, op)\nif err != nil && strings.Contains(err.Error(), \"without deleting pred\") {\n    // delete all edges for the pred, then retry the alteration\n}","preventionTips":["Check has(pred) emptiness before list->scalar changes","Keep the [] brackets intact when editing schemas","Sync ORM model array-ness with the Dgraph schema"],"tags":["dgraph","schema","list","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"}