pypa/pip · warning · OSError

is a symlink; Will not return uid for symlinks

Error message

{path} is a symlink; Will not return uid for symlinks

What it means

Raised as OSError by get_socket_standard_paths / get_uid_compat when the given path is a symlink and the OS lacks os.O_NOFOLLOW (only AIX and Jython fall into this branch). The function intentionally refuses to resolve symlinks to prevent time-of-check-time-of-use (TOCTOU) attacks: returning a uid for a symlink would let an attacker swap the target after the check. On mainstream platforms with O_NOFOLLOW the open itself fails on symlinks, so this explicit raise is the fallback path for parity.

Solutions

  1. Remove the symlink so the path points to a regular file or directory.
  2. Run pip on a mainstream CPython build that supports os.O_NOFOLLOW (Linux, macOS, Windows) instead of AIX/Jython.
  3. If the symlink is intentional and trusted, resolve it to its real target before invoking pip and pass the canonical path.
  4. Report an upstream issue if pip itself created the symlink in its cache directory.
Defensive patterns

Strategy: validation

Validate before calling

import os

def safe_uid_path(path: str) -> str | None:
    """Return a non-symlink path or None if it is a symlink."""
    if os.path.islink(path):
        return None  # caller should resolve or reject
    return path

# Before calling pip on a path, ensure it's not a symlink:
# resolved = os.path.realpath(path)

Type guard

import os

def is_safe_for_uid(path: str) -> bool:
    """True if the path is not a symlink (safe for uid lookup on AIX/Jython)."""
    return not os.path.islink(path)

Prevention

When it happens

Trigger: Calling the internal get_uid helper on a system without os.O_NOFOLLOW (AIX, old Jython) where the target path is a symbolic link. The code at compat.py:61 checks os.path.islink and raises at line 66.

Common situations: Running pip on AIX where the Python stdlib path or a package cache entry is a symlink. Legacy Jython environments. A filesystem where pip's temporary directories or wheel cache contain symlinks created by a package manager or mount point.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/a4445e448418a27c. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_internal/utils/compat.py:66

        https://github.com/pypa/pip/pull/935#discussion_r5307003

    Placed this function in compat due to differences on AIX and
    Jython, that should eventually go away.

    :raises OSError: When path is a symlink or can't be read.
    """
    if hasattr(os, "O_NOFOLLOW"):
        fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
        file_uid = os.fstat(fd).st_uid
        os.close(fd)
    else:  # AIX and Jython
        # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW
        if not os.path.islink(path):
            # older versions of Jython don't have `os.fstat`
            file_uid = os.stat(path).st_uid
        else:
            # raise OSError for parity with os.O_NOFOLLOW above
            raise OSError(f"{path} is a symlink; Will not return uid for symlinks")
    return file_uid


# The importlib.resources.open_text function was deprecated in 3.11 with suggested
# replacement we use below.
if sys.version_info < (3, 11):
    open_text_resource = importlib.resources.open_text
else:

    def open_text_resource(
        package: str, resource: str, encoding: str = "utf-8", errors: str = "strict"
    ) -> IO[str]:
        return (importlib.resources.files(package) / resource).open(
            "r", encoding=encoding, errors=errors
        )


if sys.version_info >= (3, 11):

View on GitHub (pinned to f399c37189)