OpenBMB/ChatDev · error · ValueError
script path is outside workspace root
Error message
script path is outside workspace root
What it means
WorkspaceCommandContext.resolve_under_workspace confines all paths to the workspace root: it resolves the candidate absolutely and rejects it unless it equals the root or is a descendant of it. This is a sandbox guarantee so uv-run scripts cannot escape the workspace via ../ traversal or absolute paths. Note resolve() also collapses symlinks, so symlinked locations resolving outside the root are rejected too.
Source
Thrown at functions/function_calling/uv_related.py:60
if ctx is None:
raise ValueError("_context is required for uv tools")
self.workspace_root = self._require_workspace(ctx.get("python_workspace_root"))
self._raw_ctx = ctx
@staticmethod
def _require_workspace(raw_path: Any) -> Path:
if raw_path is None:
raise ValueError("python_workspace_root missing from _context")
path = Path(raw_path).expanduser().resolve()
path.mkdir(parents=True, exist_ok=True)
return path
def resolve_under_workspace(self, relative_path: str | Path) -> Path:
candidate = Path(relative_path)
absolute = candidate if candidate.is_absolute() else self.workspace_root / candidate
absolute = absolute.expanduser().resolve()
if self.workspace_root not in absolute.parents and absolute != self.workspace_root:
raise ValueError("script path is outside workspace root")
return absolute
def _validate_packages(packages: Sequence[str]) -> List[str]:
normalized: List[str] = []
for pkg in packages:
if not isinstance(pkg, str):
raise ValueError("package entries must be strings")
stripped = pkg.strip()
if not stripped:
raise ValueError("package names cannot be empty")
if not _SAFE_PACKAGE_RE.match(stripped):
raise ValueError(f"unsafe characters detected in package spec {pkg}")
if stripped.startswith("-"):
raise ValueError(f"flags are not allowed in packages list: {pkg}")
normalized.append(stripped)
if not normalized:
raise ValueError("at least one package is required")View on GitHub (pinned to 4fb2db0ea9)
Solutions
- Move or copy the script under the workspace root and reference it relatively
- Replace symlinks that resolve outside the workspace with real files/directories inside it
- Normalize and reject '..' segments in user-supplied paths before building the final path
Example fix
# before uv_run(script="../shared/run.py") # after uv_run(script="shared/run.py") # shared/ moved/copied under workspace root
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def under_root(root, p) -> bool:
root = Path(root).resolve()
q = (root / p if not Path(p).is_absolute() else Path(p)).resolve()
return q == root or root in q.parents
if not under_root(ws_root, script):
script = copy_into_workspace(ws_root, script) Type guard
def is_within_workspace(root, candidate) -> bool:
root = Path(root).expanduser().resolve()
c = Path(candidate)
c = c if c.is_absolute() else root / c
c = c.expanduser().resolve()
return c == root or root in c.parents Try / catch
try:
uv_run(script=script, _context=ctx)
except ValueError as e:
if "outside workspace root" in str(e):
raise ValueError(f"relocate {script} inside the workspace") from e
raise Prevention
- Reject '..' segments in user-supplied script paths
- Avoid symlinks inside the workspace that point outside it
- Keep all runnable scripts under the workspace root
When it happens
Trigger: Passing a script path like "../outside/run.py" or "/etc/run.py" to uv_run; a path inside the workspace that is a symlink to a directory outside it; path built by joining user input that contains .. segments.
Common situations: Monorepo setups where the script lives in a sibling directory; symlinked workspaces (e.g. /tmp symlink on macOS) where resolve() escapes the root; naive path joins of untrusted user input.
Related errors
- Agent node '{nid}' config must be an object
- unsafe characters detected in package spec {pkg}
- flags are not allowed in packages list: {pkg}
- Invalid session_id: only letters, digits, underscores, and h
- _context is required for uv tools
AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27).
Data as JSON: /api/errors/0061886c459e1def.
Report an issue: GitHub.