benbjohnson/litestream · error
new ltx compactor: %w
Error message
new ltx compactor: %w
What it means
Hydrator.Restore constructs an ltx.NewCompactor over the opened readers; this error wraps a failure from that constructor. The LTX compactor validates its inputs (e.g. reader headers/ordering) before compacting, so a construction error means the reader set itself is invalid.
Source
Thrown at vfs.go:789
for _, info := range infos {
h.logger.Debug("opening ltx file for hydration", "level", info.Level, "min", info.MinTXID, "max", info.MaxTXID)
rc, err := h.client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, 0)
if err != nil {
return fmt.Errorf("open ltx file: %w", err)
}
rdrs = append(rdrs, rc)
}
if len(rdrs) == 0 {
return fmt.Errorf("no ltx files for hydration")
}
// Compact and decode using io.Pipe pattern
pr, pw := io.Pipe()
c, err := ltx.NewCompactor(pw, rdrs)
if err != nil {
return fmt.Errorf("new ltx compactor: %w", err)
}
c.HeaderFlags = ltx.HeaderFlagNoChecksum
h.compactor = c
go func() {
_ = pw.CloseWithError(c.Compact(ctx))
}()
h.mu.Lock()
defer h.mu.Unlock()
dec := ltx.NewDecoder(pr)
if err := dec.DecodeDatabaseTo(h.file); err != nil {
return fmt.Errorf("decode database: %w", err)
}
h.txid = infos[len(infos)-1].MaxTXID
return nilView on GitHub (pinned to 4ed7a308f6)
Solutions
- Ensure at least one non-empty LTX reader is passed; check the infos list is non-empty before Restore.
- Verify all LTX files belong to the same database (matching page size, database ID) — re-list from the correct replica path.
- Re-download the offending file to rule out truncation/corruption; compare checksums with the replica listing.
- Update litestream/ltx to matching versions if files were written by a newer writer with different header flags.
Example fix
// before
c, err := ltx.NewCompactor(pw, rdrs)
// after
if len(rdrs) == 0 { return fmt.Errorf("cannot compact: no ltx readers") }
c, err := ltx.NewCompactor(pw, rdrs)
if err != nil { return fmt.Errorf("new ltx compactor: %w", err) } Defensive patterns
Strategy: validation
Validate before calling
if len(rdrs) == 0 {
return fmt.Errorf("compactor requires at least one ltx reader")
}
for _, info := range infos {
if info.PageSize != expectedPageSize {
return fmt.Errorf("page size mismatch for ltx %d", info.MaxTXID)
}
} Try / catch
c, err := ltx.NewCompactor(pw, rdrs)
if err != nil {
return fmt.Errorf("invalid ltx reader set: %w", err)
} Prevention
- Never pass empty reader slices to the compactor
- Keep all LTX files from a single database/replica path
- Match litestream versions between writer and reader to avoid header-flag skew
When it happens
Trigger: Calling Restore with an empty reader slice (NewCompactor requires at least one reader), or LTX files whose headers cannot be validated as a coherent ordered chain during compactor setup.
Common situations: A bug or stale cache passing zero-length/empty LTX files; mixing LTX files from different databases with conflicting page sizes or timestamps; corrupted download from the object store truncated the header.
Understand the failure class
Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.
Related errors
- compaction interval must be greater than 0
- invalid replica, checksum mismatch
- ltx file corrupted
- ltx file missing
- invalid level: must be 0-%d or "all"
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/60c06338ab64efb5.
Report an issue: GitHub.