Skip to main content

Permission Denied Errors

Symptom

Command fails with “Permission denied” or “Operation not permitted” errors.

Diagnosis

  1. Check what access was granted:
  2. Query capabilities from inside the sandbox:
  3. Run with verbose logging:

Common Causes

Sensitive Paths

These paths are blocked by default, even if a parent directory is allowed:
  • ~/.ssh/ - SSH keys
  • ~/.aws/ - AWS credentials
  • ~/.gnupg/ - GPG keys
  • ~/.kube/ - Kubernetes config
  • ~/.docker/ - Docker credentials
  • Shell history files
  • Shell configuration files
Why? These paths commonly contain secrets that an AI agent should never access. If you grant one of these paths with --allow, nono will warn you on macOS:
To intentionally access a blocked path, use --bypass-protection alongside your grant:

Stale File Grants (Linux)

On Linux, a file-level grant (--read-file, --write-file, --allow-file, or profile read_file/write_file/allow_file) attaches to the file’s inode at sandbox start, not to its path. If the file is later replaced atomically — the write-temp-then-rename() pattern used by editors, git config, systemd-resolved, and most config tools — the path points to a new inode that carries no rule, and access fails with EACCES for the rest of the session even though the grant still names the path. nono why detects this from inside the sandbox and reports it:
Remedies:
  • Restart the sandbox session — grants re-resolve to the current file.
  • If the file is rewritten routinely, grant its containing directory instead: directory rules cover files created after sandbox start, so they survive atomic replacement (at the cost of a broader grant).

Network Connection Failed

Symptom

Commands like curl, wget, or API calls fail with connection errors.

Possible Causes

  1. Network explicitly blocked - You used --block-net flag
  2. Actual network issue - DNS, firewall, or connectivity problem
  3. Application-specific issue - App needs specific configuration

Solutions

  1. If you used --block-net, remove it:
  2. Check if network is allowed from inside the sandbox:
  3. Test network outside nono:

Command Not Found

Symptom

or

Solutions

  1. nono not in PATH:
  2. Target command not in PATH:

Sandbox Initialization Failed

Symptom

Linux (Landlock)

  1. Check kernel version:
  2. Check Landlock is enabled:
  3. Enable Landlock (if missing): Add lsm=landlock,... to kernel boot parameters

macOS (Seatbelt)

  1. Check macOS version:
  2. Check for SIP issues: Some system integrity protection settings can interfere. This is rare.

Dry Run Shows Different Paths

Symptom

Paths in dry-run output don’t match what you specified.

Explanation

nono canonicalizes all paths:
  • Relative paths become absolute
  • Symlinks are resolved
  • .. and . are normalized
This is intentional to prevent symlink escape attacks.

Example


Child Process Doesn’t Inherit Sandbox

Symptom

You expect a child process to be sandboxed but it seems to have more access.

This Shouldn’t Happen

Child processes always inherit sandbox restrictions. If you’re seeing this:
  1. Verify the parent is sandboxed:
  2. Check if you’re testing correctly:
If you have a reproducible case where a child escapes the sandbox, please report it as a security issue.

Performance Issues

Symptom

Commands run slower under nono.

Explanation

There is minimal overhead from sandbox initialization (microseconds). If you’re seeing significant slowdowns:
  1. First run may be slower due to path canonicalization
  2. Many file operations may show overhead from kernel permission checks

Solutions

  • Grant access to larger directories instead of many individual files
  • Use --read for directories that don’t need write access (slightly faster path)

Using nono why --self for Debugging

When running inside a nono sandbox, you can query your own capabilities using nono why --self:
The --self flag reads the sandbox state from NONO_CAP_FILE, which is the only environment variable nono sets. This is designed for programmatic introspection by AI agents rather than debugging via shell variables.

Platform-Specific Issues

macOS: “killed: 9” or Immediate Termination

This usually means the Seatbelt profile was malformed. Run with -vvv to see the generated profile:

Linux: “Landlock not supported”

Your kernel may not have Landlock enabled. Check:

Linux: Network Restrictions Not Working

Network filtering requires Landlock ABI v4 (kernel 6.7+):

Discovering Required Paths

If you’re getting permission errors and aren’t sure which paths your application needs, run the command under nono run. nono reports denied paths at exit with suggested fix flags. When running with a profile it also prompts to save a patch:

Example Workflow

  1. Run your command — nono will sandbox it and report any denials at exit:
  2. If paths were blocked, nono reports them at exit with suggested fix flags:
  3. Re-run with the suggested flags, or accept the prompt to save them as a profile patch:

WSL2-Specific Issues

Sandbox initialization: EBUSY on seccomp

Symptom: Sandbox initialization failed with EBUSY when using --capability-elevation. Cause: WSL2’s init process (PID 1) installs its own seccomp user notification filter. The Linux kernel only allows one notify listener per filter chain, so nono’s attempt to install a second returns EBUSY. Solution: nono automatically disables capability elevation on WSL2. If you see this error, ensure you’re running a recent version. This is tracked in microsoft/WSL#9548.

Credential proxy refused on WSL2

Symptom: WSL2: proxy-only network mode cannot be kernel-enforced Cause: The proxy’s network lockdown depends on seccomp notify or Landlock V4, neither of which is available on WSL2. Solutions:
  • Use --env-credential instead of --credential (env var injection works normally)
  • Opt in to degraded proxy mode with wsl2_proxy_policy: "insecure_proxy" in your profile’s security config (details)

Per-port network filtering unavailable

Symptom: --open-port or --listen-port fails on WSL2. Cause: Landlock V4 (kernel 6.7+) is required for TCP port filtering. WSL2 ships kernel 6.6. Solution: Use --block-net for full network isolation, or wait for a WSL2 kernel upgrade to 6.7+.

Verifying WSL2 compatibility

Run nono setup --check-only inside WSL2 to see the full feature availability matrix:
This reports the detected kernel version, Landlock ABI, WSL2 detection status, and which features are available.

Running Inside Docker/Podman

Proxy notification bootstrap failures

On Linux kernels without Landlock V4 networking, the CLI transfers the proxy notification listener through a short CLONE_FILES bootstrap. This handoff does not use pidfd_getfd or require CAP_SYS_PTRACE. An error mentioning pidfd_getfd failed comes from the older handoff; update nono to use the new one. A CLONE_FILES bootstrap error means startup failed before the listener handoff completed. Check that the container’s syscall policy permits seccomp user notifications, clone with CLONE_FILES, and descriptor-table detachment through either close_range with CLOSE_RANGE_UNSHARE or unshare(CLONE_FILES). The supervisor also needs access to the child’s procfs entries for notification mediation. If these requirements cannot be met, nono aborts startup instead of running the command without its restrictions.

Getting Help

If you’re still stuck:
  1. Search existing issues: GitHub Issues
  2. Open a new issue with:
    • nono version (nono --version)
    • OS and kernel version
    • Full command that failed
    • Error message
    • Output of nono run --dry-run with same flags