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
- Compare realpath of the inbox root from host vs inside container; align mounts so both resolve identically
- Avoid symlinked components in the session-data mount path
- On macOS, use /private/var-prefixed or Users-prefixed absolute paths consistently
- 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
- Use canonical absolute paths (no symlinked components) for session-data mounts
- On macOS beware /var vs /private/var divergence between host and container
- Keep host and container mount resolution identical; test with realpath on both sides
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
- inbox-safety: rejecting unsafe inbox path
- inbox-safety: failed to resolve inbox dir
- ${settingsFile} hooks.SessionStart must be an array
- engage_mode '${w.engage_mode}' can never engage on channel '
- MissingChannelAdapterError(channelType, instance)
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/8c9e0c5141e4461d.
Report an issue: GitHub.