benbjohnson/litestream · error

invalid v0.3.x snapshot filename: %q

Error message

invalid v0.3.x snapshot filename: %q

What it means

ParseSnapshotFilenameV3 extracts the integer index from a v0.3.x snapshot filename, which must match the format '{index:08x}.snapshot.lz4' (e.g. '00000000.snapshot.lz4'). This error is returned when the filename does not match the snapshotRegexV3 pattern at all, meaning the file is not recognized as a v0.3.x snapshot. Litestream throws it to defend against parsing garbage or foreign files found while scanning a v0.3.x replica layout.

Source

Thrown at v3.go:120

// FormatWALSegmentFilenameV3 returns the filename for a v0.3.x WAL segment.
// Format: {index:08x}_{offset:08x}.wal.lz4
func FormatWALSegmentFilenameV3(index int, offset int64) string {
	return fmt.Sprintf("%08x_%08x.wal.lz4", index, offset)
}

var (
	snapshotRegexV3   = regexp.MustCompile(`^([0-9a-f]{8})\.snapshot\.lz4$`)
	walSegmentRegexV3 = regexp.MustCompile(`^([0-9a-f]{8})_([0-9a-f]{8,16})\.wal\.lz4$`)
	generationRegexV3 = regexp.MustCompile(`^[0-9a-f]{16}$`)
)

// ParseSnapshotFilenameV3 parses a v0.3.x snapshot filename and returns the index.
// Returns an error if the filename does not match the expected format.
func ParseSnapshotFilenameV3(filename string) (index int, err error) {
	m := snapshotRegexV3.FindStringSubmatch(filename)
	if m == nil {
		return 0, fmt.Errorf("invalid v0.3.x snapshot filename: %q", filename)
	}
	idx, err := strconv.ParseInt(m[1], 16, 32)
	if err != nil {
		return 0, fmt.Errorf("invalid snapshot path: %s: %w", filename, err)
	}
	return int(idx), nil
}

// ParseWALSegmentFilenameV3 parses a v0.3.x WAL segment filename.
// Returns the WAL index and byte offset, or an error if the filename is invalid.
func ParseWALSegmentFilenameV3(filename string) (index int, offset int64, err error) {
	m := walSegmentRegexV3.FindStringSubmatch(filename)
	if m == nil {
		return 0, 0, fmt.Errorf("invalid v0.3.x WAL segment filename: %q", filename)
	}
	idx, err := strconv.ParseInt(m[1], 16, 32)
	if err != nil {
		return 0, 0, fmt.Errorf("invalid wal segment path: %s: %w", filename, err)

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Pass only the base filename, not the full path: use filepath.Base(path) before calling ParseSnapshotFilenameV3.
  2. Verify the filename matches the v0.3.x format '{index:08x}.snapshot.lz4'; use IsGenerationIDV3/regex or skip non-matching files when scanning a directory.
  3. If the file is an LTX file from a newer Litestream, do not use the V3 parser; use the current LTX parsing APIs instead.
  4. Check that the file is a genuine v0.3.x snapshot and not a manifest or unrelated file that landed in the snapshots/ directory.

Example fix

// before
idx, err := litestream.ParseSnapshotFilenameV3(objKey) // objKey = "generations/abcdef0123456789/snapshots/00000000.snapshot.lz4"

// after
idx, err := litestream.ParseSnapshotFilenameV3(filepath.Base(objKey))
Defensive patterns

Strategy: validation

Validate before calling

var snapshotReV3 = regexp.MustCompile(`^[0-9a-f]{8}\.snapshot\.lz4$`)
func isValidSnapshotFilenameV3(name string) bool { return snapshotReV3.MatchString(filepath.Base(name)) }

Type guard

func isV3SnapshotName(s string) bool { return snapshotReV3.MatchString(filepath.Base(s)) }
if isV3SnapshotName(key) { idx, err := litestream.ParseSnapshotFilenameV3(filepath.Base(key)) }

Try / catch

idx, err := litestream.ParseSnapshotFilenameV3(filepath.Base(name))
if err != nil {
    log.Printf("skipping non-v0.3.x snapshot %q: %v", name, err)
    return skipFile
}

Prevention

When it happens

Trigger: Calling ParseSnapshotFilenameV3 with a filename that doesn't match the v0.3.x format: a v0.3.x-era name variant, an LTX-era name like '0000000000000000-0000000000000001.ltx', a path with directories included instead of the bare filename, an uncompressed '.snapshot' (missing .lz4), or a typo'd/corrupted name.

Common situations: Migrating from Litestream v0.3.x to newer versions and scanning old replica buckets manually; custom tooling that lists replica objects and parses names; passing a full object key/path instead of the base filename; extra files (checkpoints, manifests) dropped into the snapshots directory.

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