xtekky/gpt4free · error · PermissionError

File access outside workspace is denied: '{file}'. Workspace

Error message

File access outside workspace is denied: '{file}'. Workspace: {workspace}

What it means

Raised by the sandbox's replacement open() when the requested path resolves outside the workspace directory. The shim resolves both the target and the workspace root and denies any target whose resolved path string does not start with the workspace prefix, confining .pa.py file I/O to the workspace. Note the check is a plain string prefix, so a sibling directory sharing a prefix (e.g. /ws-evil next to /ws) would also pass — a known weakness of this style of check.

Source

Thrown at g4f/mcp/pa_provider.py:538

    # Build a reduced copy of the real built-ins
    _blocked = frozenset(
        {"exec", "eval", "compile", "input", "breakpoint", "__import__"}
    )
    safe_builtins: Dict[str, Any] = {
        k: getattr(_builtins, k) for k in dir(_builtins) if k not in _blocked
    }

    # Provide a workspace-scoped open()
    def _safe_open(file, mode="r", *args, **kwargs):
        """open() restricted to the workspace directory."""
        path = Path(file)
        if not path.is_absolute():
            path = workspace / path
        try:
            resolved = path.resolve()
            ws_resolved = workspace.resolve()
            if not str(resolved).startswith(str(ws_resolved)):
                raise PermissionError(
                    f"File access outside workspace is denied: '{file}'. "
                    f"Workspace: {workspace}"
                )
        except (ValueError, OSError) as exc:
            raise PermissionError(f"Invalid file path: '{file}'") from exc
        return open(resolved, mode, *args, **kwargs)

    safe_builtins["open"] = _safe_open
    safe_builtins["__import__"] = _make_restricted_import(allowed)

    # Override print / input so stdout/stderr stay local to this sandbox
    # execution and are never written to the real sys.stdout/stderr.  This
    # avoids the global-state side-effect that contextlib.redirect_stdout
    # would cause when the thread is abandoned after a timeout.
    if stdout_buf is not None:
        _real_print = _builtins.print

        def _safe_print(*args, **kwargs):

View on GitHub (pinned to 973504e177)

Solutions

  1. Use relative paths — they are joined onto the workspace root by the shim
  2. Store any cache/credentials inside the workspace directory (e.g. open('cache.json', 'w'))
  3. Pass file contents in as strings/parameters instead of reading external files

Example fix

# before
open('/home/user/tokens.json')

# after
open('tokens.json')  # resolves inside the workspace dir
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def is_in_workspace(candidate, workspace: Path) -> bool:
    resolved = (workspace / candidate).resolve() if not Path(candidate).is_absolute() else Path(candidate).resolve()
    ws = workspace.resolve()
    return resolved == ws or str(resolved).startswith(str(ws) + str(Path('/').sep))
# note: use the sep-terminated prefix to avoid the sibling-dir bypass

Try / catch

try:
    with open(target) as f:
        data = f.read()
except PermissionError as e:
    if "outside workspace" in str(e):
        data = None  # re-point the path into the workspace

Prevention

When it happens

Trigger: open('/etc/passwd'), open('../../secrets.txt'), or any absolute/relative path whose resolved location is not under the workspace directory. Also triggered on Windows when drive letters or .. traversal escape the workspace.

Common situations: Provider code writing cache/token files to a hard-coded absolute path; using '..' to reach project files; scripts developed outside the sandbox that read config from $HOME.

Related errors


AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14). Data as JSON: /api/errors/1adf169616ac968b. Report an issue: GitHub.