dgraph-io/dgraph · error
Schema change not allowed from [%s] => %s without deleting p
Error message
Schema change not allowed from [%s] => %s without deleting pred: %s
What it means
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.
Source
Thrown at worker/mutation.go:445
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))
}
}
return nil
}
func validateSchemaForUnique(prevSchema pb.SchemaUpdate, currentSchema *pb.SchemaUpdate) error {
validTokenizer := func(tokenizers []string) bool {
for _, value := range tokenizers {
if value == "hash" || value == "exact" || value == "int" {
return trueView on GitHub (pinned to 759e242be6)
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.
Example fix
// before ALTER: tags: string @index(term) . // tags currently [string] with data // after // first delete all tags edges, then: ALTER: tags: string @index(term) .
Defensive patterns
Strategy: validation
Validate before calling
func validateListCollapse(pred string, wantList bool, d *dgo.Dgraph) error {
if schema.State().IsList(pred) && !wantList {
resp, err := d.NewReadOnlyTxn().Query(context.Background(),
fmt.Sprintf("{ q(func: has(%s), first: 1) { uid } }", pred))
if err != nil {
return err
}
if len(resp.Json) > len("{}") {
return fmt.Errorf("pred %s still has data; delete before list->scalar", pred)
}
}
return nil
} Try / catch
err := dgraph.Alter(ctx, op)
if err != nil && strings.Contains(err.Error(), "without deleting pred") {
// delete all edges for the pred, then retry the alteration
} Prevention
- Check has(pred) emptiness before list->scalar changes
- Keep the [] brackets intact when editing schemas
- Sync ORM model array-ness with the Dgraph schema
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Schema change not allowed from %s to %s
- 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/9500850f77a1904b.
Report an issue: GitHub.