benbjohnson/litestream · warning

invalid snapshot path: %s: %w

Error message

invalid snapshot path: %s: %w

What it means

After the v0.3.x snapshot filename matches the regex, ParseSnapshotFilenameV3 parses the captured index substring as a 32-bit hexadecimal integer with strconv.ParseInt. This error wraps that parse failure. It is rare in practice because the regex already restricts the capture to hex digits, but it guards against values overflowing a 32-bit int or other strconv failures.

Source

Thrown at v3.go:124

	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)
	}
	off, err := strconv.ParseInt(m[2], 16, 64)
	if err != nil {
		return 0, 0, fmt.Errorf("invalid wal segment path: %s: %w", filename, err)

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. On 32-bit platforms, check the snapshot index magnitude; indexes above 0x7FFFFFFF cannot be represented and need a 64-bit build.
  2. Verify the filename was not modified after creation; a valid v0.3.x snapshot index is at most 8 hex digits like 'ffffffff.snapshot.lz4'.
  3. If this occurs, report it as it indicates a filename that passed the regex but overflows ParseInt's 32-bit bitSize.
Defensive patterns

Strategy: type-guard

Validate before calling

func snapshotIndexFits32Bit(hexIdx string) bool {
    v, err := strconv.ParseInt(hexIdx, 16, 64)
    return err == nil && v >= 0 && v <= math.MaxInt32
}

Type guard

func fitsInt32(v int64) bool { return v >= math.MinInt32 && v <= math.MaxInt32 }

Try / catch

idx, err := litestream.ParseSnapshotFilenameV3(name)
var rangeErr *strconv.NumError
if errors.As(err, &rangeErr) {
    return rebuildOn64BitPlatform()
}

Prevention

When it happens

Trigger: Calling ParseSnapshotFilenameV3 on a platform where int is 32 bits with a hex index larger than 0x7FFFFFFF, or any internal case where the regex-matched hex string cannot be converted via ParseInt(m[1], 16, 32).

Common situations: Snapshots created with an index beyond 2^31 on 32-bit platforms (rare; only after ~2 billion WAL rotations); defensive code path that developers essentially never hit directly.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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