agentscope-ai/agentscope · error · ValueError

host_cache_dir must be a directory.

Error message

host_cache_dir must be a directory.

What it means

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.

Source

Thrown at src/agentscope/workspace/_bubblewrap/_bubblewrap_backend.py:109

            cache_dir = os.path.abspath(host_cache_dir)
            if os.path.lexists(cache_dir) and os.path.islink(cache_dir):
                raise ValueError("host_cache_dir must not be a symbolic link.")
            cache_realpath = os.path.realpath(cache_dir)
            self._host_cache_dir = cache_realpath
            mount_sources.append(("host_cache_dir", cache_realpath))
        for index, (left_name, left_path) in enumerate(mount_sources):
            for right_name, right_path in mount_sources[index + 1 :]:
                if self._paths_overlap(left_path, right_path):
                    raise ValueError(
                        f"{left_name} must not overlap {right_name}.",
                    )
        if cache_dir is not None:
            cache_created = not os.path.lexists(cache_dir)
            os.makedirs(cache_dir, mode=0o700, exist_ok=True)
            if cache_created:
                os.chmod(cache_dir, 0o700)
            if not os.path.isdir(cache_dir):
                raise ValueError("host_cache_dir must be a directory.")
            assert self._host_cache_dir is not None
            self._host_cache_identity = self._directory_identity(
                self._host_cache_dir,
                label="host_cache_dir",
            )
        self._workdir = workdir
        self._share_net = share_net
        self._env = dict(env or {})

    async def getcwd(self) -> str:
        """Return the backend's default working directory."""
        return self._workdir

    async def exec_shell(
        self,
        command: list[str],
        *,
        cwd: str | None = None,

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Remove or rename the non-directory entry at the cache path (rm /path/to/cache)
  2. Point cache_dir at a real (or not-yet-existing) directory path
  3. Ensure no symlink in the path resolves to a file

Example fix

# before
backend = BubblewrapBackend(cache_dir='/srv/cache')  # /srv/cache is a file
# after
import os, shutil
if os.path.lexists('/srv/cache') and not os.path.isdir('/srv/cache'):
    os.remove('/srv/cache')
backend = BubblewrapBackend(cache_dir='/srv/cache')
Defensive patterns

Strategy: validation

Validate before calling

import os
if os.path.lexists(cache_dir) and not os.path.isdir(cache_dir):
    os.remove(cache_dir)  # or fail fast with a clear message

Type guard

null

Try / catch

try:
    BubblewrapBackend(cache_dir=p)
except ValueError as e:
    if 'must be a directory' in str(e): ...

Prevention

When it happens

Trigger: Passing cache_dir pointing at an existing regular file, FIFO, or a dangling/non-directory filesystem entry.

Common situations: 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.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/34ebcb3a91e056bb. Report an issue: GitHub.