dgraph-io/dgraph · error
Schema change not allowed from %s to %s
Error message
Schema change not allowed from %s to %s
What it means
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.
Source
Thrown at worker/mutation.go:437
ctx := context.WithValue(context.Background(), schema.IsWrite, false)
prevSchema, _ := schema.State().Get(ctx, s.Predicate)
if err := validateSchemaForUnique(prevSchema, s); err != nil {
return err
}
}
t, err := schema.State().TypeOf(s.Predicate)
if err != nil {
// No schema previously defined, so no need to do checks about schema conversions.
return nil
}
// schema was defined already
switch {
case t.IsScalar() && (t.Enum() == pb.Posting_PASSWORD || s.ValueType == pb.Posting_PASSWORD):
// can't change password -> x, x -> password
if t.Enum() != s.ValueType {
return errors.Errorf("Schema change not allowed from %s to %s",
t.Enum(), typ.Enum())
}
case t.IsScalar() == typ.IsScalar():
// If old type was list and new type is non-list, we don't allow it until user
// has data.
if schema.State().IsList(s.Predicate) && !s.List && hasEdges(s.Predicate, math.MaxUint64) {
return errors.Errorf("Schema change not allowed from [%s] => %s without"+
" deleting pred: %s", t.Name(), typ.Name(), x.ParseAttr(s.Predicate))
}
default:
// uid => scalar or scalar => uid. Check that there shouldn't be any data.
if hasEdges(s.Predicate, math.MaxUint64) {
return errors.Errorf("Schema change not allowed from scalar to uid or vice versa"+
" while there is data for pred: %s", x.ParseAttr(s.Predicate))
}
}View on GitHub (pinned to 759e242be6)
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.
Example fix
// before userpass: string @upsert . // was previously: userpass: password // after drop pred userpass first, then: userpass: password .
Defensive patterns
Strategy: validation
Validate before calling
func validateNoPasswordChange(current pb.Posting_ValType, next pb.Posting_ValType, hasData bool) error {
if hasData && (current == pb.Posting_PASSWORD) != (next == pb.Posting_PASSWORD) {
return fmt.Errorf("cannot change type involving password with existing data")
}
return nil
} Try / catch
err := dgraph.Alter(ctx, op)
if err != nil && strings.Contains(err.Error(), "Schema change not allowed") {
// drop pred / migrate to new pred, then re-apply schema
} Prevention
- Fetch the current schema before retyping predicates
- Never retype password predicates on live data
- Use new predicate + migration pattern for credential field changes
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Schema change not allowed from [%s] => %s without deleting p
- Schema change not allowed from scalar to uid or vice versa w
- illegal rune found "%c", expecting {
- JSON map is followed by illegal rune "%c"
- Malformed JSON
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/8480c4aa8534753e.
Report an issue: GitHub.