python-poetry/poetry · error · ValueError
Refusing hardlink {member.name}: target {member.linkname} ou
Error message
Refusing hardlink {member.name}: target {member.linkname} outside {dest} What it means
Raised by extractall during manual tar validation for hardlink members (member.islnk()). Unlike symlinks, the hardlink target is resolved relative to dest itself (os.path.abspath(dest / member.linkname)) and rejected if it is not under dest. This prevents a hardlink member from binding to a file outside the extraction tree.
Source
Thrown at src/poetry/utils/helpers.py:326
member_path = Path(os.path.abspath(dest / member.name))
if not member_path.is_relative_to(dest):
raise ValueError(
f"Refusing to extract {member.name}: "
f"would write outside {dest}"
)
if member.issym():
link_target = Path(
os.path.abspath(member_path.parent / member.linkname)
)
if not link_target.is_relative_to(dest):
raise ValueError(
f"Refusing symlink {member.name}: "
f"target {member.linkname} outside {dest}"
)
elif member.islnk():
link_target = Path(os.path.abspath(dest / member.linkname))
if not link_target.is_relative_to(dest):
raise ValueError(
f"Refusing hardlink {member.name}: "
f"target {member.linkname} outside {dest}"
)
safe_members.append(member)
archive.extractall(dest, members=safe_members)
_DEPRECATED_DOWNLOAD_EXPORTS = {
"Downloader",
"download_file",
"HTTPRangeRequestSupportedError",
}
def __getattr__(name: str) -> object:
if name in _DEPRECATED_DOWNLOAD_EXPORTS:
warnings.warn(
f"Importing `{name}` from `poetry.utils.helpers` is deprecated;"View on GitHub (pinned to 92b74dcfe3)
Solutions
- Inspect `tar -tvf` for hardlink members (type '1') and strip/rebuild entries that point outside the root.
- Use a Python version with a functional tarfile.data_filter.
- Re-acquire the archive from a trusted source.
- Extract in an isolated environment to assess risk before touching real paths.
Example fix
# before
extractall(Path('pkg.tar'), dest, zip=False)
# after - vet hardlink targets
import tarfile, os
from pathlib import Path
with tarfile.open('pkg.tar') as a:
dest = Path(os.path.abspath(dest))
for m in a.getmembers():
if m.islnk():
t = Path(os.path.abspath(dest / m.linkname))
if not t.is_relative_to(dest):
raise ValueError(f'hardlink escapes dest: {m.name}')
a.extractall(dest) Defensive patterns
Strategy: validation
Validate before calling
import os, tarfile
from pathlib import Path
def tar_hardlinks_safe(source: Path, dest: Path) -> bool:
dest = Path(os.path.abspath(dest))
with tarfile.open(source) as a:
for m in a.getmembers():
if m.islnk():
t = Path(os.path.abspath(dest / m.linkname))
if not t.is_relative_to(dest):
return False
return True Try / catch
try:
extractall(source, dest, zip=False)
except ValueError as e:
# hardlink escaped dest; do not extract
raise Prevention
- Check hardlink members (type '1') in `tar -tvf` output.
- Prefer a Python with a functional data_filter.
- Reject archives with hardlinks to absolute or parent-escaping targets.
When it happens
Trigger: Extracting a tar (zip=False, no working data_filter) containing a hardlink member whose linkname, joined onto dest, resolves outside dest, e.g. a hardlink to '../../etc/shadow'.
Common situations: Crafted tarballs using hardlinks to expose host files; toolchain output that records hardlinks against absolute paths; Python pinned to a blocklisted patch level.
Related errors
- Refusing to extract {member.name}: would write outside {dest
- Refusing symlink {member.name}: target {member.linkname} out
- {root} is not a valid repository cache
- Attempting to write {path} outside of the target directory T
- Expected a value for {setting_key} setting.
AI-assisted analysis of python-poetry/poetry@92b74dcfe3 (2026-08-04).
Data as JSON: /data/errors/fca5ebd2ed629cba.json.
Report an issue: GitHub.