benbjohnson/litestream · error

encode ltx snapshot header: %w

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.

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)

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.