databendlabs/databend · error · PermissionError

Access denied: is outside allowed directories

Error message

Access denied: {target} is outside allowed directories

What it means

Raised by the script-UDF Python sandbox's _ensure_allowed guard when a file open targets a path that is not relative to any directory in ALLOWED_BASES (registered allowed roots plus interpreter prefix directories). It is a deliberate sentinel security check that blocks UDF scripts from reading or writing files outside the whitelisted directories; the offending path is reported in {target}.

Solutions

  1. Check the failing key text in the wrapped inner error and confirm the first segment is a plain decimal u64.
  2. Fix the code path that generates the seq prefix; it must write the u64 seq as the first segment.
  3. Migrate keys written by incompatible versions.

Example fix

// before
assert!("abc/queue/x".parse_key_ok());
// after
let seq: u64 = key.split('/').next().unwrap().parse().map_err(|_| skip_key())?;
Defensive patterns

Strategy: validation

Validate before calling

let seq_ok = key.split('/').next().map(|s| s.parse::<u64>().is_ok()).unwrap_or(false);
if !seq_ok { return skip(); }

Type guard

fn seq_of(key: &str) -> Option<u64> { key.split('/').next()?.parse().ok() }

Try / catch

match PermitKey::parse_key(key) { Err(e) if e.to_string().starts_with("failed to parse seq") => { log_corrupt(key); continue; }, other => other? }

Prevention

When it happens

Trigger: A key like 'notanumber/queue/x' or a seq overflowing u64 is passed to PermitKey::parse_key.

Common situations: Manually crafted keys; corrupted/truncated first segment; an older writer format that did not prefix with a numeric seq.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/e9b96f1b942c5546. Report an issue: GitHub.

Appendix: source

Thrown at src/query/script_udf_support/src/transform_udf_script.rs:586

        if path:
            ALLOWED_BASES.add(Path(path))
    for prefix in (sys.prefix, sys.exec_prefix, sys.base_prefix, sys.base_exec_prefix):
        if prefix:
            base_path = Path(prefix)
            ALLOWED_BASES.add(base_path)
            ALLOWED_BASES.add(base_path / f"lib/python{sys.version_info.major}.{sys.version_info.minor}")

    _original_open = open
    _original_os_open = os.open if hasattr(os, 'open') else None

    def _ensure_allowed(file_path: Path, target: str):
        for base in ALLOWED_BASES:
            try:
                file_path.relative_to(base)
                return
            except ValueError:
                continue
        raise PermissionError(f"Access denied: {target} is outside allowed directories")

    def safe_open(file, mode='r', **kwargs):
        file_path = Path(file).resolve()
        _ensure_allowed(file_path, file)
        return _original_open(file, mode, **kwargs)

    def safe_os_open(path, flags, mode=0o777):
        file_path = Path(path).resolve()
        _ensure_allowed(file_path, path)
        return _original_os_open(path, flags, mode)

    builtins.open = safe_open
    if _original_os_open:
        os.open = safe_os_open

    dangerous_modules = ['subprocess', 'os.system', 'eval', 'exec', 'compile']
    for module in dangerous_modules:
        if module in sys.modules:

View on GitHub (pinned to 288d84d76e)