benbjohnson/litestream · error

invalid lock downgrade: current=%s target=%s

Error message

invalid lock downgrade: current=%s target=%s

What it means

VFSFile.Lock rejected an attempt to move to a weaker lock level than currently held (e.g. EXCLUSIVE down to RESERVED). SQLite's locking protocol only escalates; a downgrade request indicates a protocol violation inside the VFS or misuse of the file handle.

Source

Thrown at vfs.go:2282

	for pgno := range f.dirty {
		if v := int64(pgno) * int64(pageSize); v > size {
			size = v
		}
	}
	f.mu.Unlock()

	f.logger.Debug("file size", "size", size)
	return size, nil
}

func (f *VFSFile) Lock(elock sqlite3vfs.LockType) error {
	f.logger.Debug("locking file", "lock", elock)

	f.mu.Lock()
	defer f.mu.Unlock()

	if elock < f.lockType {
		return fmt.Errorf("invalid lock downgrade: current=%s target=%s", f.lockType, elock)
	}

	if elock >= sqlite3vfs.LockReserved {
		// Wait for any disable operation to complete before allowing RESERVED lock.
		// This prevents new write transactions from starting during disable.
		for f.disabling {
			f.logger.Debug("waiting for disable to complete before acquiring RESERVED lock")
			f.cond.Wait()
		}

		// Reject write-intent locks when writes are disabled. Since we always
		// report OpenReadWrite to SQLite (to support cold enable), SQLite may
		// attempt write transactions even when writes are logically disabled.
		if !f.writeEnabled {
			return sqlite3vfs.ReadOnlyError
		}
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Report as a bug — SQLite itself should never issue downgrades through this path
  2. Check for concurrent misuse of the same VFS file handle
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at vfs.go:2282 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/a9f22a1e5df50904. Report an issue: GitHub.