benbjohnson/litestream · error
invalid level in path %s: %w
Error message
invalid level in path %s: %w
What it means
After confirming the key layout, parseLTXPath parses parts[1] as the compaction level integer; this error wraps strconv.Atoi failures. It means the second path segment is not a valid decimal level (e.g. garbage, empty string, or the config path prefix was not stripped so the segment is not the level at all).
Source
Thrown at nats/replica_client.go:269
return litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)
}
// parseLTXPath parses an LTX object path and returns level, minTXID, and maxTXID.
func (c *ReplicaClient) parseLTXPath(objPath string) (level int, minTXID, maxTXID ltx.TXID, err error) {
// Remove the base path prefix if present
if c.Path != "" && strings.HasPrefix(objPath, c.Path+"/") {
objPath = strings.TrimPrefix(objPath, c.Path+"/")
}
// Expected format: "ltx/<level>/<minTXID>-<maxTXID>.ltx"
parts := strings.Split(objPath, "/")
if len(parts) < 3 || parts[0] != "ltx" {
return 0, 0, 0, fmt.Errorf("invalid ltx path: %s", objPath)
}
// Parse level
if level, err = strconv.Atoi(parts[1]); err != nil {
return 0, 0, 0, fmt.Errorf("invalid level in path %s: %w", objPath, err)
}
// Parse filename (minTXID-maxTXID.ltx)
filename := parts[2]
minTXIDVal, maxTXIDVal, err := ltx.ParseFilename(filename)
if err != nil {
return 0, 0, 0, fmt.Errorf("invalid filename in path %s: %w", objPath, err)
}
return level, minTXIDVal, maxTXIDVal, nil
}
// LTXFiles returns an iterator of all LTX files on the replica for a given level.
// NATS always uses accurate timestamps from headers since they're included in LIST operations at zero cost.
// The useMetadata parameter is ignored.
func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {
if err := c.Init(ctx); err != nil {
return nil, errView on GitHub (pinned to 4ed7a308f6)
Solutions
- Check the offending key: the level segment must be a decimal integer (e.g. ltx/0/...) Set the `path` config option so the extra prefix is stripped before parsing Remove or quarantine non-litestream objects in the bucket Re-seed the replica with a fresh full backup if keys are corrupt
Example fix
// key: litestream/ltx/0/... with no path configured -> level="litestream"? actually parses level from wrong segment // before url: "nats://host:4222/backups" // after path: "litestream" url: "nats://host:4222/backups"
Defensive patterns
Strategy: validation
Validate before calling
level, err := strconv.Atoi(parts[1])
if err != nil {
log.Printf("object %q has non-numeric level segment %q; check path prefix config", key, parts[1])
} Prevention
- Ensure the `path` config prefix matches how objects were written
- Expect exactly one numeric level segment: ltx/<level>/<min>-<max>.ltx
- Clean up manually created or legacy-named objects in the bucket
When it happens
Trigger: LTXFiles → parseLTXPath where the level segment fails strconv.Atoi: keys like `ltx/abc/...`, empty segment (`ltx//...`), or unstripped prefixes shifting the segments (`litestream/ltx/0/...` with an unset Path prefix).
Common situations: Same root causes as invalid ltx path: shared buckets, wrong `path` prefix config, manually uploaded/corrupted keys; also negative or oversized level values that represent a config prefix bug.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- invalid ltx path: %s
- invalid replica, checksum mismatch
- ltx file corrupted
- ltx file missing
- parse replica url: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/dfa1755b4868b4c4.
Report an issue: GitHub.