charmbracelet/crush · error

LockFileEx: %w

Error message

LockFileEx: %w

What it means

On Windows, lockFile calls windows.LockFileEx with LOCKFILE_EXCLUSIVE_LOCK, retrying on ERROR_LOCK_VIOLATION/ERROR_IO_PENDING (contention). If LockFileEx returns any other error, the loop aborts and this error wraps the Win32 error. It indicates a real locking failure, not another holder of the lock.

Source

Thrown at internal/lock/lock_windows.go:33

// retrySleep is the interval between non-blocking lock retries in the
// blocking File path.
const retrySleep = 100 * time.Millisecond

func lockFile(ctx context.Context, f *os.File) (func(), error) {
	h := windows.Handle(f.Fd())
	for {
		ol := new(windows.Overlapped)
		flags := uint32(windows.LOCKFILE_EXCLUSIVE_LOCK | windows.LOCKFILE_FAIL_IMMEDIATELY)
		err := windows.LockFileEx(h, flags, 0, math.MaxUint32, math.MaxUint32, ol)
		if err == nil {
			return func() {
				ol := new(windows.Overlapped)
				_ = windows.UnlockFileEx(windows.Handle(f.Fd()), 0, math.MaxUint32, math.MaxUint32, ol)
			}, nil
		}
		if !errors.Is(err, windows.ERROR_LOCK_VIOLATION) && !errors.Is(err, windows.ERROR_IO_PENDING) {
			return nil, fmt.Errorf("LockFileEx: %w", err)
		}
		select {
		case <-ctx.Done():
			return nil, fmt.Errorf("acquire lock: %w", ctx.Err())
		case <-time.After(retrySleep):
		}
	}
}

func tryLockFile(f *os.File) (func(), error) {
	h := windows.Handle(f.Fd())
	ol := new(windows.Overlapped)
	flags := uint32(windows.LOCKFILE_EXCLUSIVE_LOCK | windows.LOCKFILE_FAIL_IMMEDIATELY)
	if err := windows.LockFileEx(h, flags, 0, math.MaxUint32, math.MaxUint32, ol); err != nil {
		if errors.Is(err, windows.ERROR_LOCK_VIOLATION) || errors.Is(err, windows.ERROR_IO_PENDING) {
			return nil, ErrContended
		}
		return nil, fmt.Errorf("LockFileEx: %w", err)

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Move the lock file to a local NTFS path instead of a network share.
  2. Check the wrapped Win32 error code for the exact cause (handle invalid, access denied, etc.).
  3. Exclude the data directory from antivirus/backup tools that may disrupt lock operations.
  4. Recreate the lock file if it was deleted or replaced while the process held a handle.
Defensive patterns

Strategy: fallback

Validate before calling

// keep locks off network shares on Windows
if isUNCPath(filepath.Dir(path)) {
    return fmt.Errorf("lock file must be on a local NTFS path")
}

Try / catch

release, err := lock.File(ctx, path)
if err != nil && !errors.Is(err, lock.ErrContended) {
    var errno windows.Errno
    if errors.As(err, &errno) {
        return fmt.Errorf("LockFileEx error %d: %w", errno, err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling lock.File on Windows where LockFileEx fails with an unexpected Win32 error: invalid handle, unsupported filesystem (some network shares), or an access-rights problem on the handle opened by os.OpenFile.

Common situations: Lock file located on a network share (SMB) that doesn't support byte-range locks; antivirus or backup software interfering with file locks; handle corruption after the file was deleted underneath the process.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/070cf596c00cdc81. Report an issue: GitHub.