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
- Remove the symlink so the path points to a regular file or directory.
- Run pip on a mainstream CPython build that supports os.O_NOFOLLOW (Linux, macOS, Windows) instead of AIX/Jython.
- If the symlink is intentional and trusted, resolve it to its real target before invoking pip and pass the canonical path.
- 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
- Resolve symlinks with os.path.realpath before pointing pip at directories.
- On AIX/Jython, prefer mainstream CPython if O_NOFOLLOW support is critical.
- Avoid creating symlinks inside pip's cache or build directories.
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
- Absolute paths are not supported in pylock files obtained…
- Can not open an editor for a file name containing
- Can't verify hashes for these file:// requirements because…
- Can't verify hashes for these requirements because we don't…
- In --require-hashes mode, all requirements must have their…
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)