dgraph-io/badger · error

ErrInvalidKey

ErrInvalidKey

Error message

Key is using a reserved !badger! prefix

What it means

ErrInvalidKey is returned when a user key uses the reserved '!badger!' prefix, which Badger keeps for internal keys (like !badger!head). Txn.modify rejects any key with that prefix (txn.go:360) to protect internal metadata.

Source

Thrown at errors.go:44

	// ErrTxnTooBig is returned if too many writes are fit into a single transaction.
	ErrTxnTooBig = stderrors.New("Txn is too big to fit into one request")

	// ErrConflict is returned when a transaction conflicts with another transaction. This can
	// happen if the read rows had been updated concurrently by another transaction.
	ErrConflict = stderrors.New("Transaction Conflict. Please retry")

	// ErrReadOnlyTxn is returned if an update function is called on a read-only transaction.
	ErrReadOnlyTxn = stderrors.New("No sets or deletes are allowed in a read-only transaction")

	// ErrDiscardedTxn is returned if a previously discarded transaction is reused.
	ErrDiscardedTxn = stderrors.New("This transaction has been discarded. Create a new one")

	// ErrEmptyKey is returned if an empty key is passed on an update function.
	ErrEmptyKey = stderrors.New("Key cannot be empty")

	// ErrInvalidKey is returned if the key has a special !badger! prefix,
	// reserved for internal usage.
	ErrInvalidKey = stderrors.New("Key is using a reserved !badger! prefix")

	// ErrBannedKey is returned if the read/write key belongs to any banned namespace.
	ErrBannedKey = stderrors.New("Key is using the banned prefix")

	// ErrThresholdZero is returned if threshold is set to zero, and value log GC is called.
	// In such a case, GC can't be run.
	ErrThresholdZero = stderrors.New(
		"Value log GC can't run because threshold is set to zero")

	// ErrNoRewrite is returned if a call for value log GC doesn't result in a log file rewrite.
	ErrNoRewrite = stderrors.New(
		"Value log GC attempt didn't result in any cleanup")

	// ErrRejected is returned if a value log GC is called either while another GC is running, or
	// after DB::Close has been called.
	ErrRejected = stderrors.New("Value log GC request rejected")

	// ErrInvalidRequest is returned if the user request is invalid.

View on GitHub (pinned to 2a001d466f)

Solutions

  1. Rename user keys so they don't start with '!badger!' (e.g. add an app prefix like 'app:')
  2. Sanitize/validate incoming keys before writing
  3. If you need internal data access, use the documented APIs instead of raw keys

Example fix

// before
db.Update(func(txn *Txn) error {
    return txn.SetEntry(NewEntry([]byte("!badger!head"), val)) // ErrInvalidKey
})
// after
db.Update(func(txn *Txn) error {
    return txn.SetEntry(NewEntry([]byte("app!badger!head"), val))
})
Defensive patterns

Strategy: validation

Validate before calling

if bytes.HasPrefix(key, []byte("!badger!")) {
    return fmt.Errorf("key %q uses reserved prefix", key)
}

Type guard

func isReservedKey(key []byte) bool {
    return bytes.HasPrefix(key, []byte("!badger!"))
}

Try / catch

if err := txn.Set(key, val); err != nil {
    if errors.Is(err, badger.ErrInvalidKey) {
        return fmt.Errorf("rename key %q: uses reserved !badger! prefix", key)
    }
    return err
}

Prevention

When it happens

Trigger: txn.Set/NewEntry with keys like "!badger!head" or "!badger!" (db_test.go:1124/1127); copying external data whose keys happen to start with !badger!. Note "!badger" (no trailing '!') is allowed.

Common situations: Importing datasets from other stores, generating keys from user input that collides with the prefix, probing internal keys from user code.

Related errors


AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05). Data as JSON: /api/errors/2d7db7a2ffa0c449. Report an issue: GitHub.