{"record":{"id":"34ebcb3a91e056bb","repo":"agentscope-ai/agentscope","slug":"host-cache-dir-must-be-a-directory","errorCode":null,"errorMessage":"host_cache_dir must be a directory.","messagePattern":"host_cache_dir must be a directory\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agentscope/workspace/_bubblewrap/_bubblewrap_backend.py","lineNumber":109,"sourceCode":"            cache_dir = os.path.abspath(host_cache_dir)\n            if os.path.lexists(cache_dir) and os.path.islink(cache_dir):\n                raise ValueError(\"host_cache_dir must not be a symbolic link.\")\n            cache_realpath = os.path.realpath(cache_dir)\n            self._host_cache_dir = cache_realpath\n            mount_sources.append((\"host_cache_dir\", cache_realpath))\n        for index, (left_name, left_path) in enumerate(mount_sources):\n            for right_name, right_path in mount_sources[index + 1 :]:\n                if self._paths_overlap(left_path, right_path):\n                    raise ValueError(\n                        f\"{left_name} must not overlap {right_name}.\",\n                    )\n        if cache_dir is not None:\n            cache_created = not os.path.lexists(cache_dir)\n            os.makedirs(cache_dir, mode=0o700, exist_ok=True)\n            if cache_created:\n                os.chmod(cache_dir, 0o700)\n            if not os.path.isdir(cache_dir):\n                raise ValueError(\"host_cache_dir must be a directory.\")\n            assert self._host_cache_dir is not None\n            self._host_cache_identity = self._directory_identity(\n                self._host_cache_dir,\n                label=\"host_cache_dir\",\n            )\n        self._workdir = workdir\n        self._share_net = share_net\n        self._env = dict(env or {})\n\n    async def getcwd(self) -> str:\n        \"\"\"Return the backend's default working directory.\"\"\"\n        return self._workdir\n\n    async def exec_shell(\n        self,\n        command: list[str],\n        *,\n        cwd: str | None = None,","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/workspace/_bubblewrap/_bubblewrap_backend.py#L91-L127","documentation":"BubblewrapBackend validates that host_cache_dir is a directory after creating it. If the path exists but is not a directory (e.g. a regular file, socket, or symlink to a file), makedirs succeeds as a no-op and this ValueError is raised, because the cache must be bind-mounted as a directory.","triggerScenarios":"Passing cache_dir pointing at an existing regular file, FIFO, or a dangling/non-directory filesystem entry.","commonSituations":"A stale cache file left at the expected cache path, or a symlink chain where the final target is a file; tooling that 'touches' the cache path before the library runs.","solutions":["Remove or rename the non-directory entry at the cache path (rm /path/to/cache)","Point cache_dir at a real (or not-yet-existing) directory path","Ensure no symlink in the path resolves to a file"],"exampleFix":"# before\nbackend = BubblewrapBackend(cache_dir='/srv/cache')  # /srv/cache is a file\n# after\nimport os, shutil\nif os.path.lexists('/srv/cache') and not os.path.isdir('/srv/cache'):\n    os.remove('/srv/cache')\nbackend = BubblewrapBackend(cache_dir='/srv/cache')","handlingStrategy":"validation","validationCode":"import os\nif os.path.lexists(cache_dir) and not os.path.isdir(cache_dir):\n    os.remove(cache_dir)  # or fail fast with a clear message","typeGuard":"null","tryCatchPattern":"try:\n    BubblewrapBackend(cache_dir=p)\nexcept ValueError as e:\n    if 'must be a directory' in str(e): ...","preventionTips":["Ensure the cache path is not pre-created as a file by deployment tooling"],"tags":["bubblewrap","cache","filesystem"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}