benbjohnson/litestream · error

extract timestamp from LTX header: %w

Error message

extract timestamp from LTX header: %w

What it means

uploadSizedLTX extracts the timestamp by calling ltx.PeekHeader on the reader; if the LTX header cannot be parsed, this error wraps the parse failure. It means the bytes at the start of the file are not a valid LTX header — the file is corrupt, truncated, or not an LTX file at all. The upload is aborted to avoid replicating garbage.

Source

Thrown at s3/replica_client.go:790

	return c.uploadStreamedLTX(ctx, key, r, partSize)
}

// uploadSizedLTX uploads from a seekable reader whose size is known.
func (c *ReplicaClient) uploadSizedLTX(ctx context.Context, key string, rs io.ReadSeeker, start, partSize int64) (int64, time.Time, *string, error) {
	end, err := rs.Seek(0, io.SeekEnd)
	if err != nil {
		return 0, time.Time{}, nil, fmt.Errorf("s3: size ltx file %s: %w", key, err)
	}
	size := end - start
	if _, err := rs.Seek(start, io.SeekStart); err != nil {
		return 0, time.Time{}, nil, fmt.Errorf("s3: rewind ltx file %s: %w", key, err)
	}

	// Extract timestamp from LTX header, then rewind so the upload sees the
	// full file.
	hdr, _, err := ltx.PeekHeader(rs)
	if err != nil {
		return 0, time.Time{}, nil, fmt.Errorf("extract timestamp from LTX header: %w", err)
	}
	timestamp := time.UnixMilli(hdr.Timestamp).UTC()
	if _, err := rs.Seek(start, io.SeekStart); err != nil {
		return 0, time.Time{}, nil, fmt.Errorf("s3: rewind ltx file %s: %w", key, err)
	}

	input := c.putObjectInput(key, timestamp)
	input.Body = rs

	if size < partSize {
		input.ContentLength = aws.Int64(size)
		out, err := c.s3.PutObject(ctx, input)
		if err != nil {
			return 0, time.Time{}, nil, fmt.Errorf("s3: put object %s: %w", key, err)
		}
		return size, timestamp, out.ETag, nil
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Inspect the wrapped error to see the specific header parse failure
  2. Delete the corrupt LTX file and let litestream recreate it from the live database (or run 'litestream reset' for the database to clear corrupted local LTX state)
  3. Verify disk health (fsck / SMART) if corruption is recurring
  4. Ensure nothing else writes into the replica's LTX directory

Example fix

// recover from a corrupt local LTX file
$ litestream reset /path/to/db
$ systemctl restart litestream
Defensive patterns

Strategy: validation

Validate before calling

if _, _, err := ltx.PeekHeader(f); err != nil {
    return fmt.Errorf("LTX file %s corrupt: %w", path, err)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "extract timestamp from LTX header") {
    // corrupt/truncated LTX file: remove it or run 'litestream reset' for the DB
}

Prevention

When it happens

Trigger: PeekHeader fails on a truncated LTX file (crash mid-write), a zero-length or partially initialized temp file, bit-rot on disk, or a file that was accidentally overwritten by non-LTX data.

Common situations: Host crash or power loss leaving a partial LTX file; disk corruption; buggy external tooling writing into the replica directory; restoring/moving litestream data directories incorrectly.

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