benbjohnson/litestream · error

acquire exclusive lock

Error message

acquire exclusive lock: %w

What it means

applyLTXFile could not acquire the exclusive file lock on the followed database before writing pages. Another process (a SQLite reader/writer or second litestream) holds the file, so the apply is aborted to avoid partial updates.

Solutions

  1. Stop concurrent accessors of the followed database file
  2. Ensure only one litestream follow process runs per database
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at replica.go:961 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at replica.go:961

// (bytes 18-19) to indicate DELETE journal mode instead of WAL mode, and
// randomize the schema change counter (bytes 24-27) to invalidate cached
// schemas in other connections.
func (r *Replica) applyLTXFile(ctx context.Context, f *os.File, info *ltx.FileInfo, pageSize uint32) error {
	rc, err := r.Client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, 0)
	if err != nil {
		return fmt.Errorf("open ltx file: %w", err)
	}
	defer rc.Close()

	dec := ltx.NewDecoder(rc)
	if err := dec.DecodeHeader(); err != nil {
		return fmt.Errorf("decode header: %w", err)
	}

	hdr := dec.Header()

	if err := internal.LockFileExclusive(f); err != nil {
		return fmt.Errorf("acquire exclusive lock: %w", err)
	}
	defer internal.UnlockFile(f)

	for {
		var phdr ltx.PageHeader
		data := make([]byte, pageSize)
		if err := dec.DecodePage(&phdr, data); err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("decode page: %w", err)
		}

		if phdr.Pgno == 1 && len(data) >= 28 {
			data[18], data[19] = 0x01, 0x01
			_, _ = rand.Read(data[24:28])
		}

		off := int64(phdr.Pgno-1) * int64(pageSize)

View on GitHub (pinned to 4ed7a308f6)