benbjohnson/litestream · error

extract timestamp from LTX header: %w

Error message

extract timestamp from LTX header: %w

What it means

Wraps a failure from ltx.PeekHeader when the file replica client reads an incoming LTX stream to extract its timestamp during WriteLTXFile. The header could not be parsed, meaning the payload is not a valid LTX file (empty, truncated, corrupted, or wrong format), so the write is aborted rather than persisting a file with an unknown timestamp.

Source

Thrown at file/replica_client.go:171

	return f, nil
}

// WriteLTXFile writes an LTX file to the replica.
// Extracts timestamp from LTX header and sets it as the file's ModTime to preserve original creation time.
func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (info *ltx.FileInfo, err error) {
	var fileInfo, dirInfo os.FileInfo
	if db := c.db(); db != nil {
		fileInfo, dirInfo = db.FileInfo(), db.DirInfo()
	}

	// Use TeeReader to peek at LTX header while preserving data for upload
	var buf bytes.Buffer
	teeReader := io.TeeReader(rd, &buf)

	// Extract timestamp from LTX header
	hdr, _, err := ltx.PeekHeader(teeReader)
	if err != nil {
		return nil, fmt.Errorf("extract timestamp from LTX header: %w", err)
	}
	timestamp := time.UnixMilli(hdr.Timestamp).UTC()

	// Combine buffered data with rest of reader
	fullReader := io.MultiReader(&buf, rd)

	// Ensure parent directory exists.
	filename := c.LTXFilePath(level, minTXID, maxTXID)
	if err := internal.MkdirAll(filepath.Dir(filename), dirInfo); err != nil {
		return nil, err
	}

	// Write LTX file to temporary file next to destination path.
	tmpFilename := filename + ".tmp"
	f, err := internal.CreateFile(tmpFilename, fileInfo)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Verify the producer emits complete, valid LTX data (check the DB's local LTX files).
  2. Check litestream/ltx library version compatibility between writer and replica.
  3. Re-run replication from the source; if local LTX state is corrupted, use `litestream reset`.
  4. Ensure nothing truncates the stream between producer and replica (disk space, fd limits).

Example fix

// before
rd := getReader() // may be empty
_, err := client.WriteLTXFile(ctx, level, minTXID, maxTXID, rd)
// after
data, err := io.ReadAll(getReader())
if len(data) == 0 { return fmt.Errorf("empty ltx payload, refusing write") }
_, err = client.WriteLTXFile(ctx, level, minTXID, maxTXID, bytes.NewReader(data))
Defensive patterns

Strategy: validation

Validate before calling

hdr, _, err := ltx.PeekHeader(bytes.NewReader(buf.Bytes()))
if err != nil { return fmt.Errorf("payload is not valid LTX: %w", err) }

Try / catch

if _, err := client.WriteLTXFile(ctx, level, minTXID, maxTXID, rd); err != nil {
    if strings.Contains(err.Error(), "extract timestamp from LTX header") { return fmt.Errorf("corrupt ltx stream: %w", err) }
    return err
}

Prevention

When it happens

Trigger: WriteLTXFile receiving a reader whose first bytes are not a valid LTX header: zero-byte upload, partially flushed writer, corrupted transfer, or a non-LTX payload written to a file replica.

Common situations: Disk-full or interrupted transfer producing truncated LTX data; version mismatch between the writer and the ltx package; caller passing an empty or already-consumed reader.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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