benbjohnson/litestream · error

write ltx file

Error message

write ltx file: %w

What it means

This error occurs when Replica.uploadLTXFile fails to upload a local LTX file to the replica's storage backend via Client.WriteLTXFile. The local file was already opened successfully; the failure is in writing to remote storage. The wrapped error carries the underlying client cause (network, auth, quota, etc.).

Solutions

  1. Read the wrapped cause from the message to identify the storage-client failure (auth, network, permissions).
  2. Verify replica credentials and bucket/container exist and are writable.
  3. Check network connectivity to the storage endpoint; retry after transient failures.
  4. If local LTX state is suspect, run `litestream reset` for the database to rebuild local LTX files.
Defensive patterns

Strategy: retry

Validate before calling

// preflight storage write access
_, err := client.WriteLTXFile(ctx, level, minTXID, maxTXID, bytes.NewReader(nil))
// or verify credentials/bucket reachability before starting replication

Try / catch

if err := replica.Sync(ctx); err != nil && strings.Contains(err.Error(), "write ltx file") {
    // transient storage failure: backoff and retry; check wrapped cause for auth vs network
}

Prevention

When it happens

Trigger: syncOnce (or an anonymous uploader goroutine) calls uploadLTXFile for a level/minTXID/maxTXID, the local LTX file opens fine, but r.Client.WriteLTXFile returns an error — network outage, expired cloud credentials, bucket missing, quota exceeded, or a partially-written local file.

Common situations: S3/GCS/Azure credentials expired or wrong; network partition between litestream and object storage; destination bucket deleted or permissions changed; disk returning truncated/corrupt LTX data.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at replica.go:258

	r.syncWaiters.Add(1)
	defer r.syncWaiters.Add(-1)
	if err := r.syncSem.Acquire(ctx, 1); err != nil {
		return fmt.Errorf("wait for replica sync: %w", context.Cause(ctx))
	}
	return nil
}

func (r *Replica) uploadLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID) (err error) {
	filename := r.db.LTXPath(level, minTXID, maxTXID)
	f, err := os.Open(filename)
	if err != nil {
		return NewLTXError("open", filename, level, uint64(minTXID), uint64(maxTXID), err)
	}
	defer func() { _ = f.Close() }()

	info, err := r.Client.WriteLTXFile(ctx, level, minTXID, maxTXID, f)
	if err != nil {
		return fmt.Errorf("write ltx file: %w", err)
	}
	r.Logger().Debug("ltx file uploaded",
		"level", info.Level,
		"minTXID", info.MinTXID,
		"maxTXID", info.MaxTXID,
		"size", info.Size)

	// Track current position
	//replicaWALIndexGaugeVec.WithLabelValues(r.db.Path(), r.Name()).Set(float64(rd.Pos().Index))
	//replicaWALOffsetGaugeVec.WithLabelValues(r.db.Path(), r.Name()).Set(float64(rd.Pos().Offset))

	return nil
}

// calcPos returns the last position saved to the replica for level 0.
func (r *Replica) calcPos(ctx context.Context) (pos ltx.Pos, err error) {
	info, err := r.MaxLTXFileInfo(ctx, 0)
	if err != nil {

View on GitHub (pinned to 4ed7a308f6)