{"record":{"id":"66e3b1a678cde85a","repo":"agentscope-ai/agentscope","slug":"host-cache-dir-must-not-be-a-symbolic-link-66e3b1","errorCode":null,"errorMessage":"host_cache_dir must not be a symbolic link.","messagePattern":"host_cache_dir must not be a symbolic link\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agentscope/workspace/_bubblewrap/_bubblewrap_workspace.py","lineNumber":255,"sourceCode":"                workdir.encode(\"utf-8\"),\n                digest_size=16,\n            ).hexdigest()\n            cache_root = os.path.join(\n                os.path.dirname(workdir),\n                \".agentscope-bwrap-cache\",\n            )\n            self._ensure_cache_directory(\n                cache_root,\n                make_private=True,\n            )\n            path = os.path.join(\n                cache_root,\n                key,\n            )\n            make_private = True\n\n        if os.path.lexists(path) and os.path.islink(path):\n            raise ValueError(\"host_cache_dir must not be a symbolic link.\")\n        cache_dir = os.path.realpath(path)\n        try:\n            common = os.path.commonpath([workdir, cache_dir])\n        except ValueError:\n            common = \"\"\n        if common in (workdir, cache_dir):\n            raise ValueError(\n                \"host_cache_dir must not overlap host_workdir because it \"\n                \"is used as a Bubblewrap bind source.\",\n            )\n\n        self._ensure_cache_directory(\n            path,\n            make_private=make_private,\n        )\n        return os.path.realpath(path)\n\n    @staticmethod","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/workspace/_bubblewrap/_bubblewrap_workspace.py#L237-L273","documentation":"The explicit host_cache_dir for a BubblewrapWorkspace must not be a symbolic link, because it is used directly as a Bubblewrap bind source and symlinks would escape or complicate the sandbox's path resolution. The check uses lexists+islink so it catches dangling and valid symlinks alike.","triggerScenarios":"Passing host_cache_dir that is (or contains as its final component) a symlink, e.g. /var/cache/myapp where myapp -> /mnt/cache; common on systems where /var/cache or tmpdirs are symlinked.","commonSituations":"macOS/Linux systems with symlinked cache dirs (e.g. nix, /tmp -> /private/tmp), CI setups linking cache directories, developers trying to share a cache via a link.","solutions":["Pass the realpath: host_cache_dir=os.path.realpath(p)","Remove the symlink and use a real directory","Mount the underlying target directly as host_cache_dir"],"exampleFix":"# before\nws = BubblewrapWorkspace(host_cache_dir=\"~/cache-link\")\n# after\nimport os\nws = BubblewrapWorkspace(host_cache_dir=os.path.realpath(os.path.expanduser(\"~/cache-link\")))","handlingStrategy":"validation","validationCode":"import os\nif os.path.islink(os.path.expanduser(cache_dir)):\n    cache_dir = os.path.realpath(os.path.expanduser(cache_dir))","typeGuard":"def is_real_dir(p: str) -> bool:\n    p = os.path.expanduser(p)\n    return os.path.isdir(p) and not os.path.islink(p)","tryCatchPattern":"try:\n    ws = await BubblewrapWorkspace.create(host_cache_dir=p)\nexcept ValueError as e:\n    if \"symbolic link\" in str(e):\n        p = os.path.realpath(p); retry","preventionTips":["Always realpath() user-supplied cache dirs","Avoid symlinked XDG_CACHE_HOME setups in sandboxed environments"],"tags":["bubblewrap","sandbox","symlink","cache","filesystem"],"backgroundTag":"symlink-rejected-as-path","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}