{"record":{"id":"582c796e9ad4afd2","repo":"agentscope-ai/agentscope","slug":"host-cache-dir-must-be-a-real-directory","errorCode":null,"errorMessage":"host_cache_dir must be a real directory.","messagePattern":"host_cache_dir must be a real directory\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agentscope/workspace/_bubblewrap/_bubblewrap_workspace.py","lineNumber":285,"sourceCode":"        self._ensure_cache_directory(\n            path,\n            make_private=make_private,\n        )\n        return os.path.realpath(path)\n\n    @staticmethod\n    def _ensure_cache_directory(\n        path: str,\n        *,\n        make_private: bool,\n    ) -> None:\n        \"\"\"Create a cache directory without accepting a symlink root.\"\"\"\n        if os.path.lexists(path) and os.path.islink(path):\n            raise ValueError(\"host_cache_dir must not be a symbolic link.\")\n        created = not os.path.lexists(path)\n        os.makedirs(path, mode=0o700, exist_ok=True)\n        if os.path.islink(path) or not os.path.isdir(path):\n            raise ValueError(\"host_cache_dir must be a real directory.\")\n        if created or make_private:\n            os.chmod(path, 0o700)\n\n    async def initialize(self) -> None:\n        \"\"\"Initialize and clean up partial resources on failure.\"\"\"\n        try:\n            await super().initialize()\n        except BaseException:\n            try:\n                await asyncio.shield(self.close())\n            except BaseException:\n                logger.exception(\n                    \"Bubblewrap cleanup after init failure failed\",\n                )\n            raise\n\n    async def _teardown_backend(self) -> None:\n        \"\"\"Terminate the gateway process and clean ephemeral directories.\"\"\"","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/workspace/_bubblewrap/_bubblewrap_workspace.py#L267-L303","documentation":"After makedirs, the cache path is re-validated to be a real directory (not a link, isdir true). It fires when creation raced with something replacing the path, or when the path existed as a non-directory and makedirs(exist_ok=True) did not convert it.","triggerScenarios":"host_cache_dir exists as a regular file; a dangling symlink replaced between checks; a race where another process swaps the path.","commonSituations":"Leftover cache-marker file at the cache path; tools that create a file where the workspace expects a directory; NFS/FUSE oddities where isdir is false.","solutions":["Inspect and clear the offending path: rm the file at host_cache_dir","Choose a different host_cache_dir location","Ensure no other process mutates the cache path during provisioning"],"exampleFix":"# before\n# ~/.cache/agentscope exists as a plain file\n# after\nimport os, shutil\np = os.path.expanduser(\"~/.cache/agentscope\")\nif os.path.lexists(p) and not os.path.isdir(p):\n    os.remove(p)\nws = BubblewrapWorkspace(...)","handlingStrategy":"validation","validationCode":"import os\np = os.path.realpath(os.path.expanduser(cache_dir))\nif os.path.lexists(p) and not os.path.isdir(p):\n    raise ValueError(f\"{p} exists but is not a directory\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Don't create marker files where directories are expected","Clean stale cache paths between runs in tests"],"tags":["bubblewrap","cache","filesystem","path-validation"],"backgroundTag":"path-exists-but-wrong-type","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}