{"record":{"id":"c53bc24d40452041","repo":"unslothai/unsloth","slug":"path-resolution-failed-exc","errorCode":null,"errorMessage":"path resolution failed: {exc}","messagePattern":"path resolution failed: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/utils/paths/storage_roots.py","lineNumber":384,"sourceCode":"    if \"..\" in PureWindowsPath(raw).parts:\n        return True\n    return \"..\" in raw.replace(\"\\\\\", \"/\").split(\"/\")\n\n\ndef _is_absolute_user_path(path: Path) -> bool:\n    expanded = str(path)\n    if os.name == \"nt\":\n        return path.is_absolute() and PureWindowsPath(expanded).is_absolute()\n    return path.is_absolute() and PurePosixPath(expanded).is_absolute()\n\n\ndef _assert_contained(resolved: Path, root: Path) -> None:\n    \"\"\"Raise ValueError if ``resolved`` realpaths outside ``root``.\"\"\"\n    try:\n        resolved_real = Path(os.path.realpath(resolved))\n        root_real = Path(os.path.realpath(root))\n    except OSError as exc:\n        raise ValueError(f\"path resolution failed: {exc}\") from exc\n    try:\n        resolved_real.relative_to(root_real)\n    except ValueError as exc:\n        raise ValueError(\n            f\"path escapes root: {resolved!s} -> {resolved_real!s} \" f\"is not under {root_real!s}\"\n        ) from exc\n\n\ndef resolve_under_root(\n    path_value: str | None,\n    *,\n    root: Path,\n    strip_prefixes: tuple[str, ...] = (),\n) -> Path:\n    \"\"\"Resolve ``path_value`` and assert the result is under ``root``.\n\n    Absolutes are accepted only if already contained (so pre-resolved\n    internal paths re-enter idempotently); schemas reject absolutes upstream.","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/paths/storage_roots.py#L366-L402","documentation":"Raised by _assert_contained in studio/backend/utils/paths/storage_roots.py:384-385 when os.path.realpath() raises OSError while resolving either the candidate path or the storage root. This is an environment-level failure (permissions, stale file handles, path encoding problems), not a validation failure; the error message embeds the underlying OS error.","triggerScenarios":"resolve_under_root called with a path whose realpath traversal hits an unreadable directory (EACCES), a broken symlink loop (ELOOP), or a path with invalid encoding on the platform. Also when the storage root itself becomes inaccessible (unmounted drive, deleted directory).","commonSituations":"Storage root on a disconnected network drive or removable volume on Windows; permission changes mid-session; symlink cycles created by users; deeply nested paths exceeding OS limits.","solutions":["Check the underlying OSError (__cause__) for errno: EACCES -> fix permissions, ELOOP -> break the symlink cycle, ENOENT -> the root may be gone","Verify the storage root exists and is readable before resolving paths under it","Remove or repair symlink loops in or under the root","If the volume is removable, re-mount it and retry the operation"],"exampleFix":"# before\npath = resolve_under_root(user_input, root=exports_root())\n\n# after\nimport errno\ntry:\n    path = resolve_under_root(user_input, root=exports_root())\nexcept ValueError as exc:\n    cause = exc.__cause__\n    if cause and cause.errno == errno.EACCES:\n        raise PermissionError(str(cause)) from cause\n    raise","handlingStrategy":"try-catch","validationCode":"def root_accessible(root) -> bool:\n    try:\n        root_real = Path(os.path.realpath(root))\n        return root_real.exists() and os.access(root_real, os.R_OK | os.X_OK)\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"import errno\ntry:\n    path = resolve_under_root(value, root=root)\nexcept ValueError as exc:\n    cause = exc.__cause__\n    if isinstance(cause, OSError):\n        if cause.errno in (errno.EACCES, errno.EPERM):\n            raise HTTPException(403, \"storage root not accessible\") from exc\n        if cause.errno == errno.ELOOP:\n            raise HTTPException(400, \"symlink loop under storage root\") from exc\n        raise HTTPException(503, \"storage root unavailable\") from exc\n    raise","preventionTips":["Health-check the storage root (exists + readable) at startup and before batch jobs","Avoid symlink cycles under storage roots","Surface the chained OSError errno in logs for fast triage","Mount external volumes directly, not via fragile symlink chains"],"tags":["filesystem","path-resolution","environment"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}