zylon-ai/private-gpt · error · SkillDomainError
UNSAFE_PATH_ABSOLUTE
UNSAFE_PATH_ABSOLUTE
Error message
Unsafe file path (absolute): {path!r} What it means
SkillDomainError with code UNSAFE_PATH_ABSOLUTE, raised inside _normalize_path when a file path extracted from an upload/zip is absolute (PurePosixPath(path).is_absolute() after backslash-to-forward-slash normalization). Absolute paths are rejected because stored skills must be relocatable trees rooted at SKILL.md, and absolute entries are a classic zip-slip-adjacent pattern.
Source
Thrown at private_gpt/server/skills/skills_files.py:112
f"Could not determine MIME type for file: {value.path}",
)
if "SKILL.md" not in resolved:
raise SkillDomainError(
SkillErrorCode.MISSING_SKILL_MD, "Upload must include a SKILL.md file."
)
return list(resolved.values())
def _normalize_path(path: str) -> str:
# Normalize backslashes to forward slashes
path = path.replace("\\", "/")
parsed = PurePosixPath(path)
if parsed.is_absolute():
raise SkillDomainError(
SkillErrorCode.UNSAFE_PATH_ABSOLUTE,
f"Unsafe file path (absolute): {path!r}",
)
if ".." in parsed.parts:
raise SkillDomainError(
SkillErrorCode.UNSAFE_PATH_TRAVERSAL,
f"Unsafe file path (path traversal): {path!r}",
)
parts = list(parsed.parts)
if parts and parts[-1].lower() == "skill.md":
parts[-1] = "SKILL.md"
return "/".join(parts)
_DEFAULT_MIME_TYPE = "application/octet-stream"
View on GitHub (pinned to 4a030776a3)
Solutions
- Rebuild the archive with relative paths: `cd project-root && zip -r ../skill.zip .`.
- Strip leading slashes when generating entries: entry.lstrip('/').
- If ingesting third-party archives, pre-scan with zipfile and reject/sanitize absolute member names before upload.
- Audit the packaging step (CI script, GUI zipper) that produced absolute entry names.
Example fix
# before
zip.write('/home/user/skill/SKILL.md') # stores absolute entry '/home/user/skill/SKILL.md'
# after
os.chdir('/home/user/skill')
for f in Path('.').rglob('*'):
zip.write(f, arcname=str(f)) # relative entries Defensive patterns
Strategy: validation
Validate before calling
from pathlib import PurePosixPath
def entries_are_relative(names: list[str]) -> bool:
return all(not PurePosixPath(n.replace('\\', '/')).is_absolute() for n in names) Type guard
const isRelative = (entry: string): boolean =>
!entry.replace(/\\/g, '/').startsWith('/'); Prevention
- Always create archives from inside the intended root directory.
- Use arcname when calling ZipFile.write to force relative entry names.
- For untrusted archives, sanitize or reject absolute members before submitting.
When it happens
Trigger: A zip containing an entry like '/etc/passwd', '/tmp/x.txt', or on Windows-style archives '\\C:\\data\\file.txt' (backslashes are normalized to '/' first, making '/C:/data/file.txt' absolute). Any leading slash in an entry name triggers it.
Common situations: Zips produced by tools that store absolute paths (some tar/zip converters, PowerShell Compress-Archive with absolute inputs); malicious or corrupted archives; test fixtures hand-crafted with leading slashes.
Related errors
- UNSAFE_PATH_TRAVERSAL
- 'allOf' must be an array of schemas
- BUNDLE_TOO_LARGE
- MISSING_SKILL_MD
- MISSING_FILES
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/1741ac3fab3786be.
Report an issue: GitHub.