dgraph-io/dgraph · error
Schema change not allowed from scalar to uid or vice versa w
Error message
Schema change not allowed from scalar to uid or vice versa while there is data for pred: %s
What it means
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.
Source
Thrown at worker/mutation.go:452
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 true
}
}
return false
}
if prevSchema.Predicate != "" {
if prevSchema.Upsert && !currentSchema.Upsert {
return errors.Errorf("could not drop @upsert from [%v] predicate when @unique directive specified",View on GitHub (pinned to 759e242be6)
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.
Example fix
// before ALTER: owner: string . // owner is uid and has data // after // delete all owner edges (or drop pred owner), then: ALTER: owner: string .
Defensive patterns
Strategy: validation
Validate before calling
func validateUidScalarSwap(pred string, d *dgo.Dgraph) error {
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 has data; uid<->scalar change forbidden", pred)
}
return nil
} Try / catch
err := dgraph.Alter(ctx, op)
if err != nil && strings.Contains(err.Error(), "scalar to uid or vice versa") {
// drop pred (or delete all edges), then apply new type
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Index not allowed on predicate of type uid on predicate %s
- Cannot reverse for non-uid type on predicate %s
- Schema change not allowed from %s to %s
- Schema change not allowed from [%s] => %s without deleting p
- illegal rune found "%c", expecting {
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/cd1e7a8dc7fdb5ae.
Report an issue: GitHub.