benbjohnson/litestream · error

decode ltx header: %w

Error message

decode ltx header: %w

What it means

Litestream opened the LTX file but the ltx.Decoder failed while decoding its header. This means the bytes at the start of the object are not a valid LTX header — corruption, truncation, or the object is not actually an LTX file.

Source

Thrown at vfs.go:2812

			return 0, fmt.Errorf("unsupported page size: %d", pageSize)
		}
		return pageSize, nil
	}
	if lastErr != nil {
		return 0, fmt.Errorf("read ltx header: %w", lastErr)
	}
	return 0, fmt.Errorf("no ltx file available to determine page size")
}

func readPageSizeFromInfo(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (uint32, error) {
	rc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)
	if err != nil {
		return 0, fmt.Errorf("open ltx file: %w", err)
	}
	defer rc.Close()
	dec := ltx.NewDecoder(rc)
	if err := dec.DecodeHeader(); err != nil {
		return 0, fmt.Errorf("decode ltx header: %w", err)
	}
	return dec.Header().PageSize, nil
}

func isSupportedPageSize(pageSize uint32) bool {
	switch pageSize {
	case 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536:
		return true
	default:
		return false
	}
}

func (f *VFSFile) waitForRestorePlan() ([]*ltx.FileInfo, error) {
	// If write mode is enabled, don't wait - return immediately so we can
	// create a new database if no files exist.
	if f.writeEnabled {
		infos, err := CalcRestorePlan(f.ctx, f.client, 0, time.Time{}, f.logger)

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Delete the corrupted LTX object and re-replicate from the source database
  2. Run litestream reset to clear local state and force a fresh snapshot
  3. Verify with litestream ltx that the object parses and check its header checksum
  4. Check LTX_FORMAT.md for format-version compatibility between writer and reader versions

Example fix

// before: truncated object fails DecodeHeader
// after: validate remotely before restore
$ litestream ltx -level all s3://bucket/path  # list + verify each LTX file
$ litestream restore -output db.sqlite db.yml
Defensive patterns

Strategy: try-catch

Try / catch

if err := openVFS(ctx); err != nil {
    if strings.Contains(err.Error(), "decode ltx header") {
        return quarantineAndReset(ctx) // litestream reset + re-replicate
    }
    return err
}

Prevention

When it happens

Trigger: readPageSizeFromInfo calling dec.DecodeHeader() on a stream whose header is invalid: partial uploads, objects overwritten/corrupted by another writer, or a non-LTX object stored under the replica prefix.

Common situations: Interrupted uploads leaving truncated objects; manually copied/renamed files in the bucket; version-mismatch where an old LTX format is read by a newer reader (upgrade artifacts); proxy/storage layer returning an HTML error page instead of object bytes.

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