dgraph-io/badger · critical
Must have caught a nil callback for txn.CommitWith
Error message
Must have caught a nil callback for txn.CommitWith
What it means
This panic guards runTxnCallback against a txnCb whose user callback field is nil. CommitWith is supposed to reject nil callbacks up front, so reaching this panic means the nil check in CommitWith was bypassed or the txnCb was constructed without a user function. It indicates an internal invariant violation, not normal user-facing error handling.
Source
Thrown at txn.go:685
// If batchSet failed, LSM would not have been updated. So, no need to rollback anything.
// TODO: What if some of the txns successfully make it to value log, but others fail.
// Nothing gets updated to LSM, until a restart happens.
return txnCb()
}
type txnCb struct {
commit func() error
user func(error)
err error
}
func runTxnCallback(cb *txnCb) {
switch {
case cb == nil:
panic("txn callback is nil")
case cb.user == nil:
panic("Must have caught a nil callback for txn.CommitWith")
case cb.err != nil:
cb.user(cb.err)
case cb.commit != nil:
err := cb.commit()
cb.user(err)
default:
cb.user(nil)
}
}
// CommitWith acts like Commit, but takes a callback, which gets run via a
// goroutine to avoid blocking this function. The callback is guaranteed to run,
// so it is safe to increment sync.WaitGroup before calling CommitWith, and
// decrementing it in the callback; to block until all callbacks are run.
func (txn *Txn) CommitWith(cb func(error)) {
if cb == nil {
panic("Nil callback provided to CommitWith")
}View on GitHub (pinned to 2a001d466f)
Solutions
- Always pass a concrete non-nil func(error) to CommitWith — e.g. cb := func(error){}; txn.CommitWith(cb)
- Upgrade Badger: current versions make CommitWith's nil check unconditionally hit first
- If wrapping callbacks in interfaces, check reflect.ValueOf(cb).IsNil() to catch typed-nil funcs
Example fix
// before
var cb func(error)
txn.CommitWith(cb) // nil, panics
// after
txn.CommitWith(func(err error) { if err != nil { log.Error(err) } }) Defensive patterns
Strategy: validation
Validate before calling
if cb == nil { return errors.New("CommitWith requires a non-nil callback") }
txn.CommitWith(cb) Type guard
func isNilFunc(cb func(error)) bool { return cb == nil || reflect.ValueOf(cb).IsNil() } Try / catch
defer func(){ if r := recover(); r != nil && strings.Contains(fmt.Sprint(r), "nil callback") { /* restore and re-create txn */ } }() Prevention
- Always construct the callback inline at the CommitWith call site
- Beware typed-nil func values when callbacks come from interfaces
When it happens
Trigger: Calling CommitWith with a nil func(error) on a code path where the CommitWith nil-check is skipped (patched/forked code), or a txnCb built with cb.user == nil passed to runTxnCallback.
Common situations: Custom forks of Badger; testing harnesses that call runTxnCallback directly; subtle bugs where a nil-typed func value is wrapped (typed nil interface) and slips past an `if cb == nil` check in CommitWith.
Related errors
- txn callback is nil
- Nil callback provided to CommitWith
- Base level can't be zero.
- BlockCacheSize should be set since compression/encryption ar
- Cannot use GetSequence with managedDB=true.
AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05).
Data as JSON: /api/errors/12db56d6578d9db3.
Report an issue: GitHub.