{"record":{"id":"74fe2815c97b57e0","repo":"benbjohnson/litestream","slug":"compaction-not-enabled","errorCode":null,"errorMessage":"compaction not enabled","messagePattern":"compaction not enabled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":3003,"sourceCode":"\tif f.vfs.L0Retention > 0 {\n\t\tf.compactionWg.Add(1)\n\t\tgo func() {\n\t\t\tdefer f.compactionWg.Done()\n\t\t\tf.monitorL0Retention(f.compactionCtx)\n\t\t}()\n\t}\n\n\tf.logger.Info(\"compaction monitors started\",\n\t\t\"levels\", len(levels),\n\t\t\"snapshotInterval\", f.vfs.SnapshotInterval,\n\t\t\"l0Retention\", f.vfs.L0Retention)\n}\n\n// Compact compacts source level files into the destination level.\n// Returns ErrNoCompaction if there are no files to compact.\nfunc (f *VFSFile) Compact(ctx context.Context, level int) (*ltx.FileInfo, error) {\n\tif f.compactor == nil {\n\t\treturn nil, fmt.Errorf(\"compaction not enabled\")\n\t}\n\treturn f.compactor.Compact(ctx, level)\n}\n\n// Snapshot creates a full database snapshot from remote LTX files.\n// Unlike DB.Snapshot(), this reads from remote rather than local WAL.\nfunc (f *VFSFile) Snapshot(ctx context.Context) (*ltx.FileInfo, error) {\n\tif f.compactor == nil {\n\t\treturn nil, fmt.Errorf(\"compaction not enabled\")\n\t}\n\n\tf.mu.Lock()\n\tpageSize := f.pageSize\n\tcommit := f.commit\n\tpos := f.pos\n\tpages := make(map[uint32]ltx.PageIndexElem, len(f.index))\n\tfor pgno, elem := range f.index {\n\t\tpages[pgno] = elem","sourceCodeStart":2985,"sourceCodeEnd":3021,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2985-L3021","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use the DB-layer compaction API (db.go) instead of VFSFile.Compact when no VFS compactor is configured","Configure the VFS file with a compactor at open time if you need VFS-driven compaction","Check ErrNoCompaction handling — even with a compactor, zero files to compact returns that sentinel","Verify your config actually enables the compaction levels you call"],"exampleFix":"// before: compacting a restore-mode VFS file\ninfo, err := vfsFile.Compact(ctx, 1) // compaction not enabled\n// after: compact via the DB layer\ninfo, err := db.Compact(ctx, 0, 1)","handlingStrategy":"validation","validationCode":"if vfsFile.Compactor() == nil {\n    return fmt.Errorf(\"vfs file has no compactor; use db.Compact instead\")\n}","typeGuard":"func canCompact(f *VFSFile) bool { return f != nil && f.Compactor() != nil }","tryCatchPattern":"info, err := vfsFile.Compact(ctx, level)\nif err != nil {\n    if err.Error() == \"compaction not enabled\" {\n        return db.Compact(ctx, level, level+1)\n    }\n    if errors.Is(err, litestream.ErrNoCompaction) {\n        return nil // nothing to compact; not fatal\n    }\n    return err\n}","preventionTips":["Configure a compactor on the VFS file if you plan to compact via VFS","Use DB-layer compaction for embed use cases","Treat ErrNoCompaction as a no-op, not a failure"],"tags":["vfs","compaction","unsupported"],"backgroundTag":"feature-not-enabled","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}