EveryInc/compound-engineering-plugin · warning
Skipping ${storeDir}: resolves outside the managed root via
Error message
Skipping ${storeDir}: resolves outside the managed root via a symlinked ancestor (not modified) What it means
storeRootEscapesManagedRoot in src/targets/managed-artifacts.ts guards against a symlinked ancestor directory making the managed store path resolve outside the writer's target root (e.g. ~/.codex). If the resolved store directory is not within rootDir, every cleanup and write that would act through that link is skipped, because they would otherwise modify whatever external location the user pointed the link at.
Source
Thrown at src/targets/managed-artifacts.ts:323
const rel = path.relative(rootResolved, targetResolved)
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel))
}
export async function isPathWithinRoot(rootDir: string, targetPath: string): Promise<boolean> {
return isContainedPath(await realpathNearestExisting(rootDir), await realpathNearestExisting(targetPath))
}
/**
* Guards a managed store directory against ancestor-symlink traversal. Returns
* true (and warns once) when `storeDir` escapes `rootDir` via a symlinked
* ancestor, meaning the caller must skip EVERY operation on that store --
* cleanup sweeps and writes alike -- since all of them would otherwise act
* through the link into whatever the user pointed it at. `rootDir` is the
* writer's target root (e.g. `~/.codex`), not the store dir itself.
*/
export async function storeRootEscapesManagedRoot(rootDir: string, storeDir: string): Promise<boolean> {
if (await isPathWithinRoot(rootDir, storeDir)) return false
console.warn(`Skipping ${storeDir}: resolves outside the managed root via a symlinked ancestor (not modified)`)
return true
}
View on GitHub (pinned to c9c10f8c75)
Solutions
- If the redirection is intentional, accept the warning: the converter intentionally will not follow the link; manage that location manually or via your dotfiles tooling.
- If the symlink is accidental, remove or replace it with a real directory (mkdir after rm) and re-run the install.
- If you want per-item links instead, install normally first, then replace individual managed items with symlinks — they will then be preserved per the user-managed-symlink rule.
- Verify with `readlink -f <storeDir>` that it resolves where you expect before re-running.
Example fix
// before ls -la ~/.codex/skills # symlink -> /data/shared/skills // after rm ~/.codex/skills mkdir ~/.codex/skills bun run convert --to codex
Defensive patterns
Strategy: validation
Validate before calling
import { realpath } from 'node:fs/promises'
import path from 'node:path'
async function storeWithinRoot(rootDir: string, storeDir: string): Promise<boolean> {
const root = await realpath(rootDir).catch(() => rootDir)
const store = await realpath(path.dirname(storeDir)).catch(() => path.dirname(storeDir))
return store === root || store.startsWith(root + path.sep)
}
// if false, a symlinked ancestor redirects the store outside the root Prevention
- Avoid symlinking intermediate directories under the target root (~/.codex/skills etc.); link individual files only if needed
- Run `readlink -f ~/.codex <storeDir>` before installing to confirm resolution stays inside the root
- Beware symlinked HOME directories when the tool compares literal vs resolved paths
When it happens
Trigger: Calling any of promptsEscaped, skillsEscaped, agentsEscaped, commandsEscaped, pluginsEscaped, or extensionsEscaped when isPathWithinRoot(rootDir, storeDir) returns false — i.e. storeDir (or an ancestor) is a symlink resolving outside the target root such as ~/.codex.
Common situations: Users symlink ~/.codex/skills or ~/.codex/agents to a directory elsewhere (dotfiles sync, shared machines, secondary disks); users whose HOME is symlinked (e.g. /home/user -> /usr/home/user) so realpath resolution moves the store outside the nominal root.
Related errors
- Skipping ${targetPath}: existing user-managed symlink (not o
- Skipping ${targetPath}: existing user-managed symlink (not o
- Skipping ${targetDir}: existing user-managed symlink (not ov
- ${context.collectionPath} exists and is not a symlink; refus
- ${context.collectionPath} points outside a Compound Engineer
AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31).
Data as JSON: /api/errors/de8c153ea13d73ee.
Report an issue: GitHub.