nanocoai/nanoclaw · warning
agent-route: failed to inspect source outbox dir
Error message
agent-route: failed to inspect source outbox dir
What it means
While forwarding attachments in an agent-to-agent routed message, the host failed to inspect (realpath) the source session's outbox attachment directory. realpathSync threw — typically ENOENT because the directory no longer exists, or EACCES/EMMLINK on pathological mounts. The failure is non-fatal: forwarding returns no files and the message goes on without attachments.
Source
Thrown at src/modules/agent-to-agent/agent-route.ts:89
sourceMsgId: source.messageId,
sourceDir,
});
return [];
}
let realSourceDir: string;
try {
const sourceDirStat = fs.lstatSync(sourceDir);
if (!sourceDirStat.isDirectory() || sourceDirStat.isSymbolicLink()) {
log.warn('agent-route: rejecting unsafe source outbox dir', {
sourceMsgId: source.messageId,
sourceDir,
});
return [];
}
realSourceDir = fs.realpathSync(sourceDir);
} catch (err) {
log.warn('agent-route: failed to inspect source outbox dir', {
sourceMsgId: source.messageId,
sourceDir,
err,
});
return [];
}
// Target-side containment — shared with the channel-inbound path. A
// compromised target agent can write inside its own session dir, so it could
// pre-place `inbox` (or `inbox/<future-msgId>`) as a symlink pointing
// anywhere host-writable; ensureContainedInboxDir refuses the symlink before
// any copy lands outside the sandbox (#2828, CWE-59).
const inboxRoot = path.join(sessionDir(target.agentGroupId, target.sessionId), 'inbox');
const targetInboxDir = ensureContainedInboxDir(inboxRoot, target.messageId, {
targetGroup: target.agentGroupId,
targetSession: target.sessionId,
targetMsgId: target.messageId,
});View on GitHub (pinned to 294ef2aee8)
Solutions
- Check that the source session dir data/v2-sessions/<group>/<session>/ still exists when the forward runs (sweep ordering)
- Verify the messageId being forwarded actually has attachments recorded in the source outbox DB
- If mounts are involved, confirm the host process has read+execute on every path component
- Rate-limit or serialize session cleanup so in-flight forwards finish first
Example fix
// before
realSourceDir = fs.realpathSync(sourceDir);
// after
realSourceDir = fs.existsSync(sourceDir) ? fs.realpathSync(sourceDir) : null;
if (!realSourceDir) { log.warn('source outbox missing', { sourceDir }); return []; } Defensive patterns
Strategy: validation
Validate before calling
const exists = fs.existsSync(sourceDir) && fs.statSync(sourceDir).isDirectory(); if (!exists) return []; // skip forwarding
Try / catch
catch (err) { if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []; throw err; } Prevention
- Serialize session cleanup behind in-flight forwards
- Log sourceDir alongside messageId to correlate with sweep timing
When it happens
Trigger: routeAgentMessage forwards a message whose source messageId references attachments under data/v2-sessions/<source-session>/attachments; the source session directory was deleted/swept before forwarding ran, or a mount/permission change made the path unreadable.
Common situations: Session retention sweep racing with a delayed a2a forward; manually deleted session dirs; group folder moved between host restarts; NFS/sync mounts with flaky realpath behavior.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- inbox-safety: failed to resolve inbox dir
- Unknown provider: ${name}. Registered: ${known}
- engage_mode '${w.engage_mode}' can never engage on channel '
- MissingChannelAdapterError(channelType, instance)
- chat-sdk bridge instance ${JSON.stringify(config.instance)}
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/24dd8f4460334d5b.
Report an issue: GitHub.