{"record":{"id":"8480c4aa8534753e","repo":"dgraph-io/dgraph","slug":"schema-change-not-allowed-from-s-to-s","errorCode":null,"errorMessage":"Schema change not allowed from %s to %s","messagePattern":"Schema change not allowed from (.+?) to (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"worker/mutation.go","lineNumber":437,"sourceCode":"\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\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}","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/worker/mutation.go#L419-L455","documentation":"Once a predicate has data, Dgraph forbids changing its type to or from the password type (and more generally disallowed scalar-type changes) because existing postings were encoded with the old type and cannot be reinterpreted. checkSchema compares the existing schema type t with the requested type and errors when the change involves pb.Posting_PASSWORD and the types differ.","triggerScenarios":"Altering a predicate that already has data from password to another scalar type (e.g. 'password: string' where it was 'password: password'), or into password from another type, via ALTER while old postings exist.","commonSituations":"Renaming or retyping a credential field during refactors; switching from Dgraph's password type to hashed strings stored as plain string; schema cleanups on production databases with existing users.","solutions":["Drop the predicate first (drop all data for the predicate / alter to delete), then apply the new type.","Create a new predicate with the desired type and migrate data with an upsert/DQL rewrite, then drop the old one.","If the types truly match (both password), confirm ValueType equals the existing t.Enum() so no change is requested.","Do type-change planning offline: read schema.State() and diff before proposing the mutation."],"exampleFix":"// before\nuserpass: string @upsert .   // was previously: userpass: password\n// after\ndrop pred userpass first, then:\nuserpass: password .","handlingStrategy":"validation","validationCode":"func validateNoPasswordChange(current pb.Posting_ValType, next pb.Posting_ValType, hasData bool) error {\n    if hasData && (current == pb.Posting_PASSWORD) != (next == pb.Posting_PASSWORD) {\n        return fmt.Errorf(\"cannot change type involving password with existing data\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"err := dgraph.Alter(ctx, op)\nif err != nil && strings.Contains(err.Error(), \"Schema change not allowed\") {\n    // drop pred / migrate to new pred, then re-apply schema\n}","preventionTips":["Fetch the current schema before retyping predicates","Never retype password predicates on live data","Use new predicate + migration pattern for credential field changes"],"tags":["dgraph","schema","password","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"}