benbjohnson/litestream · error

compaction not enabled

Error message

compaction not enabled

What it means

VFSFile.Compact compacts LTX files between levels, but only if a compactor was configured on the file. When f.compactor is nil (compaction was never wired up for this VFS file), the call returns this error. Compaction is optional in the VFS path, so absence is reported explicitly rather than panicking.

Source

Thrown at vfs.go:3003

	if f.vfs.L0Retention > 0 {
		f.compactionWg.Add(1)
		go func() {
			defer f.compactionWg.Done()
			f.monitorL0Retention(f.compactionCtx)
		}()
	}

	f.logger.Info("compaction monitors started",
		"levels", len(levels),
		"snapshotInterval", f.vfs.SnapshotInterval,
		"l0Retention", f.vfs.L0Retention)
}

// Compact compacts source level files into the destination level.
// Returns ErrNoCompaction if there are no files to compact.
func (f *VFSFile) Compact(ctx context.Context, level int) (*ltx.FileInfo, error) {
	if f.compactor == nil {
		return nil, fmt.Errorf("compaction not enabled")
	}
	return f.compactor.Compact(ctx, level)
}

// Snapshot creates a full database snapshot from remote LTX files.
// Unlike DB.Snapshot(), this reads from remote rather than local WAL.
func (f *VFSFile) Snapshot(ctx context.Context) (*ltx.FileInfo, error) {
	if f.compactor == nil {
		return nil, fmt.Errorf("compaction not enabled")
	}

	f.mu.Lock()
	pageSize := f.pageSize
	commit := f.commit
	pos := f.pos
	pages := make(map[uint32]ltx.PageIndexElem, len(f.index))
	for pgno, elem := range f.index {
		pages[pgno] = elem

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Use the DB-layer compaction API (db.go) instead of VFSFile.Compact when no VFS compactor is configured
  2. Configure the VFS file with a compactor at open time if you need VFS-driven compaction
  3. Check ErrNoCompaction handling — even with a compactor, zero files to compact returns that sentinel
  4. Verify your config actually enables the compaction levels you call

Example fix

// before: compacting a restore-mode VFS file
info, err := vfsFile.Compact(ctx, 1) // compaction not enabled
// after: compact via the DB layer
info, err := db.Compact(ctx, 0, 1)
Defensive patterns

Strategy: validation

Validate before calling

if vfsFile.Compactor() == nil {
    return fmt.Errorf("vfs file has no compactor; use db.Compact instead")
}

Type guard

func canCompact(f *VFSFile) bool { return f != nil && f.Compactor() != nil }

Try / catch

info, err := vfsFile.Compact(ctx, level)
if err != nil {
    if err.Error() == "compaction not enabled" {
        return db.Compact(ctx, level, level+1)
    }
    if errors.Is(err, litestream.ErrNoCompaction) {
        return nil // nothing to compact; not fatal
    }
    return err
}

Prevention

When it happens

Trigger: Calling VFSFile.Compact(ctx, level) on a VFS file opened without compaction support — e.g. a read-only restore-mode VFS file, or an open path that does not construct the compactor.

Common situations: Embedding app calls Compact on VFS files created for restore/read paths only; assuming VFS files share DB-layer compaction settings; feature flag or config section omitted so no compactor is built.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/74fe2815c97b57e0. Report an issue: GitHub.