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, err

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. 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

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


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