dgraph-io/badger · critical
Base level can't be zero.
Error message
Base level can't be zero.
What it means
fillTablesL0ToLbase panics if the compaction's next level is 0; L0->Lbase compaction must target a base level above 0. Reaching this panic indicates an internal invariant violation in level-controller state (baseLevel computation went wrong), often due to corrupted options or manifest state.
Source
Thrown at levels.go:1256
cd.thisRange = infRange
cd.top = out
// Avoid any other L0 -> Lbase from happening, while this is going on.
thisLevel := s.cstatus.levels[cd.thisLevel.level]
thisLevel.ranges = append(thisLevel.ranges, infRange)
for _, t := range out {
s.cstatus.tables[t.ID()] = struct{}{}
}
// For L0->L0 compaction, we set the target file size to max, so the output is always one file.
// This significantly decreases the L0 table stalls and improves the performance.
cd.t.fileSz[0] = math.MaxUint32
return true
}
func (s *levelsController) fillTablesL0ToLbase(cd *compactDef) bool {
if cd.nextLevel.level == 0 {
panic("Base level can't be zero.")
}
// We keep cd.p.adjusted > 0.0 here to allow functions in db.go to artificially trigger
// L0->Lbase compactions. Those functions wouldn't be setting the adjusted score.
if cd.p.adjusted > 0.0 && cd.p.adjusted < 1.0 {
// Do not compact to Lbase if adjusted score is less than 1.0.
return false
}
cd.lockLevels()
defer cd.unlockLevels()
top := cd.thisLevel.tables
if len(top) == 0 {
return false
}
var out []*table.Table
if len(cd.dropPrefixes) > 0 {
// Use all tables if drop prefix is set. We don't want to compact only aView on GitHub (pinned to 2a001d466f)
Solutions
- Do not call internal doCompact/fillTablesL0ToLbase directly with hand-built compactionPriority structs
- Verify MaxLevels and level size options are sane so baseLevel > 0 is computed
- Update badger to a version with the level-controller fixes if the panic occurs during normal operation
Example fix
// before
db.lc.doCompact(173, compactionPriority{level: 0, score: 1.73}) // baseLevel unset -> 0 -> panic
// after
db.lc.doCompact(173, compactionPriority{level: 0, score: 1.73, t: db.lc.levelTargets(), baseLevel: 1}) // ensure baseLevel >= 1 Defensive patterns
Strategy: fallback
Validate before calling
// Go: only trigger compactions through public APIs // use db.RunValueLogGC / automatic compaction instead of db.lc.doCompact(...)
Try / catch
// Go
func triggerCompact(db *badger.DB) (err error) {
defer func() { if r := recover(); r != nil { err = fmt.Errorf("compaction panic: %v", r) } }()
return db.Flatten(2) // safe public alternative
} Prevention
- Do not call internal levelsController methods with hand-built compactionPriority
- Let automatic compaction schedule work; use db.Flatten for manual compaction
- Keep level options (MaxLevels, baseLevelSize, size ratios) consistent
- Upgrade badger if this panic occurs during normal operation
When it happens
Trigger: Internal compaction scheduling computing nextLevel == 0 for an L0 compaction; induced compactions (like doCompact with a hand-made compactionPriority) where baseLevel wasn't set; MaxLevels/level-target options resulting in a zero base level.
Common situations: Custom code calling db.lc.doCompact or starting compactions with malformed compactionPriority; corrupted level-controller state after option changes (e.g. NumLevelZeroTables thresholds, MaxLevels).
Related errors
- txn callback is nil
- Must have caught a nil callback for txn.CommitWith
- Cannot have 1 compactor. Need at least 2
- 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/89892486a515e051.
Report an issue: GitHub.