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

  1. Restart the predicate move from scratch so the full stream (schema first) is delivered.
  2. Verify the sender iterates the badger stream with schema keys ordered first (default IterationOrder for predicate moves).
  3. Ensure source and destination Alpha versions match.
  4. 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

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


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/9dcfcf963a6587fe. Report an issue: GitHub.