dgraph-io/dgraph · error
Expecting first key to be schema key: %+v
Error message
Expecting first key to be schema key: %+v
What it means
During a predicate move, the destination group expects the FIRST KV streamed by the source leader to be the predicate's schema key (so it can clean the old predicate and set schema at kv.Version). If x.Parse succeeds but pk.IsSchema() is false, this error aborts the move, because applying data keys before the schema would leave the destination inconsistent.
Source
Thrown at worker/predicate_move.go:81
proposal := &pb.Proposal{}
size := 0
var pk x.ParsedKey
for kvPayload := range kvs {
buf := z.NewBufferSlice(kvPayload.GetData())
err := buf.SliceIterate(func(s []byte) error {
kv := &bpb.KV{}
x.Check(proto.Unmarshal(s, kv))
if len(pk.Attr) == 0 {
// This only happens once.
var err error
pk, err = x.Parse(kv.Key)
if err != nil {
return errors.Errorf("while parsing kv: %+v, got error: %v", kv, err)
}
if !pk.IsSchema() {
return errors.Errorf("Expecting first key to be schema key: %+v", kv)
}
// Delete on all nodes. Remove the schema at timestamp kv.Version-1 and set it at
// kv.Version. kv.Version will be the TxnTs of the predicate move.
p := &pb.Proposal{CleanPredicate: pk.Attr, StartTs: kv.Version - 1}
glog.Infof("Predicate being received: %v", pk.Attr)
if err := n.proposeAndWait(ctx, p); err != nil {
glog.Errorf("Error while cleaning predicate %v %v\n", pk.Attr, err)
return err
}
}
proposal.Kv = append(proposal.Kv, kv)
size += len(kv.Key) + len(kv.Value)
if size >= 32<<20 { // 32 MB
if err := n.proposeAndWait(ctx, proposal); err != nil {
return err
}View on GitHub (pinned to 759e242be6)
Solutions
- Restart the predicate move from scratch so the full stream (schema first) is delivered.
- Verify the sender iterates the badger stream with schema keys ordered first (default IterationOrder for predicate moves).
- Ensure source and destination Alpha versions match.
- If using custom tooling, prepend the schema KV for the predicate before data KVs.
Example fix
// before: streaming data KVs only
streamKVs(predicate, opts{SkipSchema: true})
// after
streamKVs(predicate, opts{SkipSchema: false}) // schema key must come first Defensive patterns
Strategy: validation
Validate before calling
pk, err := x.Parse(kv.Key)
if err == nil && !pk.IsSchema() {
return errors.New("stream must start with the predicate's schema key")
} Type guard
func isSchemaKey(key []byte) bool {
pk, err := x.Parse(key)
return err == nil && pk.IsSchema()
} Try / catch
if strings.Contains(err.Error(), "Expecting first key to be schema key") {
// restart the move from scratch so schema KV is streamed first
} Prevention
- Always stream the schema KV first when moving a predicate
- Use the built-in MovePredicate path rather than custom KV replay
- Do not resume interrupted moves at arbitrary offsets
- Pin identical Dgraph versions on both Alphas involved in the move
When it happens
Trigger: ReceivePredicate/batchAndProposeKeyValues on the destination Alpha receives a stream whose first KV is a data/posting key instead of a schema key — e.g. a sender that doesn't order schema first, a move implemented by external tooling replaying KVs, or a partial move restarted mid-stream skipping the schema KV.
Common situations: Custom/patched sender code streaming tablet KVs without the schema key prefix; resuming an interrupted move at the wrong offset; version mismatch altering key ordering between Alphas.
Related errors
- while parsing kv: %+v, got error: %v
- illegal rune found "%c", expecting {
- JSON map is followed by illegal rune "%c"
- Malformed JSON
- error creating indexer for %s: %w
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/9dcfcf963a6587fe.
Report an issue: GitHub.