benbjohnson/litestream · error

encode ltx snapshot header

Error message

encode ltx snapshot header: %w

What it means

Wraps a failure to encode the LTX snapshot header (version, page size, commit, WAL offset/size, salts, timestamp) into the snapshot stream. Encoding writes bytes through the pipe, so failures mean the pipe consumer has closed/cancelled or the header fields were rejected as invalid by the encoder. It aborts the entire snapshot stream via pw.CloseWithError.

Solutions

  1. Check the consumer for an earlier error or premature close; the root cause is usually on the reading side
  2. Ensure pageSize/commit values are initialized before starting a snapshot (the caller already guards pageSize==0)
  3. Retry the snapshot with a stable, unclosed reader and sufficient timeout
  4. If header values look invalid, verify the database was opened and initialized before replication
Defensive patterns

Strategy: try-catch

Validate before calling

// verify db initialized (page size set) before snapshot
if st := db.SyncStatus(); st == dbStatusOffline { return errors.New("db not initialized") }

Try / catch

err := streamSnapshot(ctx, db)
if err != nil && strings.Contains(err.Error(), "encode ltx snapshot header") {
    // almost always a consumer-side early close/cancel; inspect consumer first
    return fmt.Errorf("snapshot consumer aborted: %w", err)
}

Prevention

When it happens

Trigger: enc.EncodeHeader(ltx.Header{...}) fails during snapshot generation: pipe reader closed early, context-cancelled consumer, or an invalid header field (e.g. zero/invalid PageSize because pageSize was not initialized).

Common situations: Consumer stops reading on an earlier error leaving the producer to fail on the next write; restore timeout elapsing mid-header; a pageSize of 0 slipping through due to a racing database initialization.

Related errors


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

Appendix: source

Thrown at db.go:2880

		enc, err := ltx.NewEncoder(pw)
		if err != nil {
			pw.CloseWithError(fmt.Errorf("new ltx encoder: %w", err))
			return
		}
		if err := enc.EncodeHeader(ltx.Header{
			Version:   ltx.Version,
			Flags:     ltx.HeaderFlagNoChecksum,
			PageSize:  uint32(pos.pageSize),
			Commit:    commit,
			MinTXID:   1,
			MaxTXID:   pos.pos.TXID,
			Timestamp: time.Now().UnixMilli(),
			WALOffset: walOffset,
			WALSize:   walSize,
			WALSalt1:  rd.salt1,
			WALSalt2:  rd.salt2,
		}); err != nil {
			pw.CloseWithError(fmt.Errorf("encode ltx snapshot header: %w", err))
			return
		}

		if err := db.writeLTXFromDB(ctx, enc, walFile, commit, pageMap); err != nil {
			pw.CloseWithError(fmt.Errorf("write snapshot ltx: %w", err))
			return
		}

		if err := enc.Close(); err != nil {
			pw.CloseWithError(fmt.Errorf("close ltx snapshot encoder: %w", err))
			return
		}
		_ = pw.Close()
	}()

	return &snapshotReadCloser{PipeReader: pr, pos: pos}, nil
}

View on GitHub (pinned to 4ed7a308f6)