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
- Rename user keys so they don't start with '!badger!' (e.g. add an app prefix like 'app:')
- Sanitize/validate incoming keys before writing
- 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
- Namespace all user keys with an app-specific prefix (e.g. 'app:')
- Sanitize externally imported keys before writing
- Add a reserved-prefix check to your key-construction tests
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.