benbjohnson/litestream · error

invalid ltx path: %s

Error message

invalid ltx path: %s

What it means

parseLTXPath converts a NATS object key into (level, minTXID, maxTXID) and expects the layout `ltx/<level>/<minTXID>-<maxTXID>.ltx` (after stripping the configured Path prefix). This error means the object key does not match that layout, so it cannot be interpreted as an LTX segment.

Source

Thrown at nats/replica_client.go:264

	return nil
}

// ltxPath returns the object path for an LTX file.
func (c *ReplicaClient) ltxPath(level int, minTXID, maxTXID ltx.TXID) string {
	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.

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Inspect the offending object key in the bucket (`nats object ls backups`) and remove non-LTX objects
  2. Fix the `path` prefix in the litestream config so `ltx/...` keys parse after prefix stripping
  3. Ensure litestream (not other tools) owns this bucket — use a dedicated bucket
  4. If keys came from a legacy layout, re-snapshot with a full backup and `litestream reset`

Example fix

// config
// before (prefix mismatch: objects stored under "litestream/ltx/...")
url: "nats://host:4222/backups"
// after
path: "litestream"
url: "nats://host:4222/backups"
Defensive patterns

Strategy: validation

Validate before calling

// validate object keys before parsing
parts := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
if len(parts) < 3 || parts[0] != "ltx" {
	log.Printf("skipping non-LTX object %q in replica bucket", key)
}

Prevention

When it happens

Trigger: LTXFiles → parseLTXPath on an object whose key has fewer than 3 slash-separated parts or whose first part is not `ltx`: foreign objects uploaded to the bucket, wrong Path prefix in config, or legacy/differently-named keys.

Common situations: Users or other tools storing non-LTX objects in the replica bucket; misconfigured `path` prefix in litestream config so the prefix strip fails; buckets shared between different tooling; keys created by older litestream layouts.

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/017f17b5b90cb461. Report an issue: GitHub.