NousResearch/hermes-agent · error · ValueError

path is outside the allowed workspace

Error message

path is outside the allowed workspace

What it means

A file reference (@file: path in a message) resolved to a path outside the allowed workspace root. The _resolve_path helper in agent/context_references.py expands the target (including ~), resolves symlinks, and requires the result to be relative to allowed_root when one is supplied. This is a workspace-containment guard for reference expansion, which the gateway feeds with untrusted remote message text.

Source

Thrown at agent/context_references.py:480

    raw = await web_extract_tool([url], format="markdown")
    payload = json.loads(raw)
    docs = payload.get("results", [])
    if not docs:
        return ""
    doc = docs[0]
    return str(doc.get("content") or doc.get("raw_content") or "").strip()


def _resolve_path(cwd: Path, target: str, *, allowed_root: Path | None = None) -> Path:
    path = Path(os.path.expanduser(target))
    if not path.is_absolute():
        path = cwd / path
    resolved = path.resolve()
    if allowed_root is not None:
        try:
            resolved.relative_to(allowed_root)
        except ValueError as exc:
            raise ValueError("path is outside the allowed workspace") from exc
    return resolved


def _ensure_reference_path_allowed(path: Path) -> None:
    from hermes_constants import get_hermes_home
    home = Path(os.path.expanduser("~")).resolve()
    hermes_home = get_hermes_home().resolve()

    blocked_exact = {home / rel for rel in _SENSITIVE_HOME_FILES}
    blocked_exact.add(hermes_home / ".env")
    blocked_dirs = [home / rel for rel in _SENSITIVE_HOME_DIRS]
    blocked_dirs.extend(hermes_home / rel for rel in _SENSITIVE_HERMES_DIRS)

    if path in blocked_exact:
        raise ValueError("path is a sensitive credential file and cannot be attached")

    for blocked_dir in blocked_dirs:
        try:

View on GitHub (pinned to c896c09c42)

Solutions

  1. Reference only files inside the session's working directory / allowed workspace root.
  2. Copy the needed file into the workspace before attaching it.
  3. If the file legitimately lives elsewhere, adjust the session's working directory (terminal.cwd in config.yaml) so the allowed root covers it.
  4. Check for symlinks in the path (ls -l) — a symlink resolving outside the workspace triggers this even if the link itself is inside.

Example fix

# before — outside workspace
@file:~/notes/secret-steps.md
# after — copy into the workspace first, then reference
cp ~/notes/secret-steps.md ./secret-steps.md
@file:./secret-steps.md
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def is_within_workspace(target: str, allowed_root: Path) -> bool:
    p = Path(os.path.expanduser(target))
    if not p.is_absolute():
        p = Path.cwd() / p
    try:
        p.resolve().relative_to(allowed_root.resolve())
        return True
    except ValueError:
        return False

Try / catch

try:
    expand_references(message)
except ValueError as e:
    if "outside the allowed workspace" in str(e):
        # tell the user to move the file into the workspace
        ...

Prevention

When it happens

Trigger: A message containing @file:../../etc/passwd or @file:/etc/hosts when the allowed_root is the session cwd; a symlink inside the workspace that resolves outside it (Path.resolve() follows symlinks); an absolute path to any location outside the workspace root.

Common situations: Attempting to attach system files or home-directory files via @file: references in a workspace-scoped session (CLI or gateway); symlinked project directories pointing outside the workspace; remote chat peers probing with absolute paths.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/0e909f8587ce3910. Report an issue: GitHub.