nanocoai/nanoclaw · warning

inbox-safety: inbox dir escaped inbox root

Error message

inbox-safety: inbox dir escaped inbox root

What it means

After mkdir, realpath of the created inbox dir does not sit inside realpath of the inbox root — meaning the resolved location escaped the sandbox. The function returns null and attachment extraction is refused for that message.

Source

Thrown at src/inbox-safety.ts:75

  for (const dir of [inboxRoot, inboxDir]) {
    try {
      const st = fs.lstatSync(dir);
      if (st.isSymbolicLink() || !st.isDirectory()) {
        log.warn('inbox-safety: rejecting unsafe inbox path', { ...context, dir });
        return null;
      }
    } catch {
      // Does not exist yet — fine, mkdir below creates it.
    }
  }

  fs.mkdirSync(inboxDir, { recursive: true });

  try {
    const realInboxDir = fs.realpathSync(inboxDir);
    const realInboxRoot = fs.realpathSync(inboxRoot);
    if (!isPathInside(realInboxRoot, realInboxDir)) {
      log.warn('inbox-safety: inbox dir escaped inbox root', { ...context, inboxDir });
      return null;
    }
    return realInboxDir;
  } catch (err) {
    log.warn('inbox-safety: failed to resolve inbox dir', { ...context, inboxDir, err });
    return null;
  }
}

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Compare realpath of the inbox root from host vs inside container; align mounts so both resolve identically
  2. Avoid symlinked components in the session-data mount path
  3. On macOS, use /private/var-prefixed or Users-prefixed absolute paths consistently
  4. Reproduce with realpath on both sides, then adjust the mount source/target
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
import path from 'node:path';
function resolvesInside(root: string, child: string): boolean {
  const r = fs.realpathSync(root);
  const c = fs.realpathSync(child);
  return c === r || c.startsWith(r + path.sep);
}

Prevention

When it happens

Trigger: ensureContainedInboxDir: realpathSync(inboxDir) resolves outside realpathSync(inboxRoot), e.g. because inboxRoot itself traverses a symlinked parent that realpaths elsewhere while inboxDir was created on a different bind mount.

Common situations: Docker bind mounts where host and container resolve the same logical path to different real paths; macOS /var vs /private/var symlink divergence between the two realpath calls; overlapping mounts shadowing the inbox root.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/8c9e0c5141e4461d. Report an issue: GitHub.