{"record":{"id":"be1a2a6ca07f36d6","repo":"dgraph-io/badger","slug":"txn-callback-is-nil","errorCode":null,"errorMessage":"txn callback is nil","messagePattern":"txn callback is nil","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"txn.go","lineNumber":683,"sourceCode":"\t\treturn err\n\t}\n\t// If batchSet failed, LSM would not have been updated. So, no need to rollback anything.\n\n\t// TODO: What if some of the txns successfully make it to value log, but others fail.\n\t// Nothing gets updated to LSM, until a restart happens.\n\treturn txnCb()\n}\n\ntype txnCb struct {\n\tcommit func() error\n\tuser   func(error)\n\terr    error\n}\n\nfunc runTxnCallback(cb *txnCb) {\n\tswitch {\n\tcase cb == nil:\n\t\tpanic(\"txn callback is nil\")\n\tcase cb.user == nil:\n\t\tpanic(\"Must have caught a nil callback for txn.CommitWith\")\n\tcase cb.err != nil:\n\t\tcb.user(cb.err)\n\tcase cb.commit != nil:\n\t\terr := cb.commit()\n\t\tcb.user(err)\n\tdefault:\n\t\tcb.user(nil)\n\t}\n}\n\n// CommitWith acts like Commit, but takes a callback, which gets run via a\n// goroutine to avoid blocking this function. The callback is guaranteed to run,\n// so it is safe to increment sync.WaitGroup before calling CommitWith, and\n// decrementing it in the callback; to block until all callbacks are run.\nfunc (txn *Txn) CommitWith(cb func(error)) {\n\tif cb == nil {","sourceCodeStart":665,"sourceCodeEnd":701,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/txn.go#L665-L701","documentation":"runTxnCallback is Badger's internal dispatcher for commit callbacks used by CommitWith. It panics if the *txnCb struct itself is nil, which indicates an internal wiring bug in the commit path rather than user error. This panic runs inside a goroutine spawned by CommitWith, so it crashes the process if reached.","triggerScenarios":"A nil *txnCb reaches runTxnCallback; this only happens via internal misuse of the callback plumbing (e.g. committing an empty txn whose callback path constructs no txnCb, or a library bug/patched code path passing nil).","commonSituations":"Using CommitWith on a transaction with no pending writes combined with managed/Read-only txn setups in older versions; custom forks that call runTxnCallback directly.","solutions":["Check the Badger version for known bugs in the CommitWith empty-transaction path and upgrade","Ensure you never call CommitWith with a nil callback (that produces error 52 before this one)","If using a fork, verify runTxnCallback is only invoked with a non-nil *txnCb allocated by CommitWith"],"exampleFix":"// before (fork/internal)\nrunTxnCallback(nil)\n// after\nif cb != nil { runTxnCallback(cb) }","handlingStrategy":"validation","validationCode":"if cb == nil { cb = func(error){} } // before any commit path involving CommitWith","typeGuard":null,"tryCatchPattern":"defer func(){ if r := recover(); r != nil { log.Fatalf(\"badger txn callback panic: %v\", r) } }() // around goroutines is not possible; guard at CommitWith call site","preventionTips":["Never pass nil to CommitWith","Keep CommitWith usage to the public API only; do not call runTxnCallback in forks"],"tags":["panic","goroutine","internal-invariant","commit-callback"],"backgroundTag":"nil-callback-panic","analyzedSha":"2a001d466f6b71a917319a1db41f99860e16e269","analyzedAt":"2026-09-05T13:00:02.264Z","contentChangedAt":"2026-09-05T13:00:02.264Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}