benbjohnson/litestream · critical
TODO(ltx): Re-implement after multi-level compaction
Error message
TODO(ltx): Re-implement after multi-level compaction
What it means
Replica.EnforceRetention is currently a stub: multi-level compaction work in the LTX rewrite left retention enforcement unimplemented, so the method panics with a TODO marker. The original retention logic (listing/removing old snapshots and WAL files) is commented out. Any caller invoking it on this version of the code will crash.
Source
Thrown at replica.go:319
// Pos returns the current replicated position.
// Returns a zero value if the current position cannot be determined.
func (r *Replica) Pos() ltx.Pos {
r.mu.RLock()
defer r.mu.RUnlock()
return r.pos
}
// SetPos sets the current replicated position.
func (r *Replica) SetPos(pos ltx.Pos) {
r.mu.Lock()
defer r.mu.Unlock()
r.pos = pos
}
// EnforceRetention forces a new snapshot once the retention interval has passed.
// Older snapshots and WAL files are then removed.
func (r *Replica) EnforceRetention(ctx context.Context) (err error) {
panic("TODO(ltx): Re-implement after multi-level compaction")
/*
// Obtain list of snapshots that are within the retention period.
snapshots, err := r.Snapshots(ctx)
if err != nil {
return fmt.Errorf("snapshots: %w", err)
}
retained := FilterSnapshotsAfter(snapshots, time.Now().Add(-r.Retention))
// If no retained snapshots exist, create a new snapshot.
if len(retained) == 0 {
snapshot, err := r.Snapshot(ctx)
if err != nil {
return fmt.Errorf("snapshot: %w", err)
}
retained = append(retained, snapshot)
}
View on GitHub (pinned to 4ed7a308f6)
Solutions
- Stop calling EnforceRetention until the TODO is implemented; manage retention on the replica backend (e.g. S3 lifecycle rules) instead
- Pin to the last version before the LTX rewrite if you need programmatic EnforceRetention
- Check store.go / current config options for replacement retention settings; upgrade litestream if a later release re-implements retention
Example fix
// before
if err := replica.EnforceRetention(ctx); err != nil { ... } // panics
// after: rely on storage lifecycle policy instead
// e.g. S3 bucket lifecycle rule: expire *.ltx older than 24h Defensive patterns
Strategy: validation
Validate before calling
// Go: guard the call site against the stub implementation
// TODO was flagged in source: replica.go EnforceRetention panics on this version.
if retentionEnforcedExplicitly {
return errors.New("EnforceRetention is unimplemented in this litestream version; use storage lifecycle rules")
} Try / catch
// panic-based stub: recover defensively if you must call it
func safeEnforceRetention(ctx context.Context, r *litestream.Replica) (err error) {
defer func() { if p := recover(); p != nil { err = fmt.Errorf("EnforceRetention unavailable: %v", p) } }()
return r.EnforceRetention(ctx)
} Prevention
- Do not call EnforceRetention directly in this version; rely on storage-level retention (S3 lifecycle, etc.)
- Pin litestream versions and read release notes for the LTX rewrite's API removals
- Check the source for panic("TODO") stubs before depending on a method
- If retention is required, upgrade to a release that re-implements it or manage it externally
When it happens
Trigger: Any call to (*litestream.Replica).EnforceRetention(ctx) on this source revision — e.g. external code that used the library API to enforce retention, or store loops still wired to it — hits the panic unconditionally.
Common situations: Upgrading to the LTX-era version of litestream while code (or a fork/monitor) still calls EnforceRetention directly; expecting retention to run automatically and instead getting a crash; depending on the removed retention behavior after a version change.
Related errors
- snapshot retention must be greater than 0
- l0 retention must not be negative
- l0 retention check interval must be greater than 0
- fetch ltx files: %w
- remove ltx files: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/b28b1b796c6dd678.
Report an issue: GitHub.