Skip to content

Windows retry policy treats permanent PermissionDenied errors as transient file locks #14682

Description

@zkochan

pnpm-fs's Windows retry policy treats every PermissionDenied error as a transient file lock:

https://github.com/pnpm/pnpm/blob/main/pnpm/crates/fs/src/retry.rs

pub(crate) fn is_transient_file_lock_error(error: &io::Error) -> bool {
    cfg!(windows)
        && (matches!(error.kind(), io::ErrorKind::PermissionDenied | io::ErrorKind::ResourceBusy)
            || matches!(error.raw_os_error(), Some(ERROR_SHARING_VIOLATION | ERROR_LOCK_VIOLATION)))
}

ERROR_ACCESS_DENIED (5) maps to PermissionDenied, and a scanner holding a handle is only one of the things that produces it. A read-only file, a restrictive ACL, or a directory occupying the destination path produces the same error and will never clear. Those failures consume the full RETRY_BUDGET of one minute before the operation returns the error it already had on the first attempt.

A native Windows probe with a directory occupying the destination returned error 5 after 603 attempts and 60.0004645 seconds. The directory and its child were preserved, so the outcome is correct; only the latency is wrong.

Every caller of the policy is affected: rename_with_retry, remove_dir_all_with_retry, and remove_file_with_retry. #14573 widened the exposure by routing command-shim removal and the shim replacement rename through it, and it is why bin_cleanup_and_replacement_preserve_deletion_errors is ignored on Windows.

What to decide:

  • Whether a sharing or lock violation (32, 33) plus ResourceBusy is the right transient set, and what to do about error 5, which is genuinely ambiguous. A rename of a directory blocked by an open handle below it reports 5 and is transient, which is why it is in the set today.
  • Whether the answer differs per operation. Removal and rename may warrant different classifiers than recursive directory removal.
  • Whether a permanent failure should fail fast or keep a much shorter budget.

Whatever lands needs native Windows tests that a read-only file and a restrictive ACL fail fast, alongside the existing transient-lock recovery tests, and bin_cleanup_and_replacement_preserve_deletion_errors should then run on Windows instead of being ignored.


Written by an agent (Claude Code, claude-opus-5).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions