langchain-ai/deepagents · error · ValueError
media path escapes outbound root: {path}
Error message
media path escapes outbound root: {path} What it means
ValueError raised by resolve_bounded_media_path when the strictly-resolved real path of the file is not contained within the resolved outbound root. This catches symlink escapes: the file itself may exist, but resolving symlinks leads outside the root.
Source
Thrown at libs/talon/deepagents_talon/media.py:234
require_relative
and (path.is_absolute() or windows_path.is_absolute() or is_windows_drive_path)
):
msg = f"media path must be a local relative path under the outbound root: {path}"
raise ValueError(msg)
if ".." in path.parts or ".." in windows_path.parts:
msg = f"media path must not contain parent-directory traversal: {path}"
raise ValueError(msg)
root_resolved = root.expanduser().resolve()
candidate_input = root_resolved / path if not path.is_absolute() else path
try:
candidate = candidate_input.resolve(strict=True)
except OSError as exc:
msg = f"media file is unavailable: {path}"
raise ValueError(msg) from exc
if not candidate.is_relative_to(root_resolved):
msg = f"media path escapes outbound root: {path}"
raise ValueError(msg)
if not candidate.is_file():
msg = f"media path is not a regular file: {path}"
raise ValueError(msg)
return candidate
def _document_parts(paths: list[Path]) -> list[str]:
parts: list[str] = []
for path in paths:
if path.suffix.lower() not in READABLE_DOCUMENT_EXTENSIONS:
parts.append(f"_(Received unsupported document attachment: {path.name}.)_")
continue
try:
if path.stat().st_size > MAX_TEXT_DOCUMENT_BYTES:
parts.append(f"_(Document attachment is too large to read inline: {path.name}.)_")
continue
content = path.read_text(encoding="utf-8", errors="replace")
except OSError:View on GitHub (pinned to a1af029e6e)
Solutions
- Copy the file into the outbound root instead of symlinking it
- Point the symlink target inside the outbound root
- If intentional, adjust the root to the common ancestor containing the real file
Example fix
// before ln -s /var/data/shared.png $OUTBOUND_ROOT/shared.png # symlink escapes root // after cp /var/data/shared.png $OUTBOUND_ROOT/shared.png # real file inside root
Defensive patterns
Strategy: validation
Validate before calling
root_resolved = root.expanduser().resolve()
try:
candidate = (root_resolved / relative).resolve(strict=True)
except OSError:
candidate = None
if candidate is None or not candidate.is_relative_to(root_resolved):
raise ValueError(f"path escapes root: {relative}") Type guard
def stays_within_root(p: Path, root: Path) -> bool:
try:
return p.expanduser().resolve(strict=True).is_relative_to(root.expanduser().resolve())
except OSError:
return False Try / catch
try:
resolved = resolve_bounded_media_path(path, root)
except ValueError as exc:
logger.warning("Media escapes outbound root: %s", exc)
return None Prevention
- Copy files into the outbound root instead of symlinking
- Audit outbound directories for symlinks before send
- If symlinks are required, ensure targets resolve inside the root (or widen root to the common ancestor)
When it happens
Trigger: A symlink inside the outbound root pointing to /etc/passwd or any file outside root; hardlinked or symlinked directories resolving outward.
Common situations: Symlinking shared assets into the outbound folder for convenience; CI artifacts that are symlinks into a cache directory; container volume setups with linked paths.
Related errors
- media path must be a local filesystem path: {path}
- media path must be a local relative path under the outbound
- media path must not contain parent-directory traversal: {pat
- debug log directory is not a real directory: {path}
- Skill path {skill_path} resolves outside all allowed skill d
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/044de6ed9b7a55e5.
Report an issue: GitHub.