EveryInc/compound-engineering-plugin · error · Error
${context.collectionPath} exists and is not a symlink; refus
Error message
${context.collectionPath} exists and is not a symlink; refusing to overwrite it What it means
activateLocalCollection creates $CODEX_HOME/skills/compound-engineering-local as a symlink to the checkout's skills directory. Before linking it inspects the existing path; if something non-symlink already occupies that location (a real directory or file) it is reported as a "collision" and the library refuses to overwrite, protecting user data at ~/.codex/skills.
Source
Thrown at src/dev/codex-dev.ts:266
} catch {
return { kind: "unrelated", target, resolvedTarget }
}
if (path.basename(resolvedTarget) !== "skills") {
return { kind: "unrelated", target, resolvedTarget }
}
return { kind: "valid", target, resolvedTarget }
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") return { kind: "broken", target }
throw error
}
}
export async function activateLocalCollection(context: CodexDevContext): Promise<void> {
await fs.mkdir(path.dirname(context.collectionPath), { recursive: true })
const desiredTarget = await fs.realpath(context.skillsRoot)
const state = await inspectLocalCollection(context)
if (state.kind === "collision") {
throw new Error(`${context.collectionPath} exists and is not a symlink; refusing to overwrite it`)
}
if (state.kind === "unrelated") {
throw new Error(`${context.collectionPath} points outside a Compound Engineering checkout; refusing to overwrite it`)
}
if (state.kind === "broken") {
throw new Error(`${context.collectionPath} is a broken symlink; refusing to overwrite it`)
}
if (state.kind === "valid" && state.resolvedTarget === desiredTarget) return
await replaceManagedCollectionLink(context.collectionPath, desiredTarget, state)
}
export type ManagedCollectionLinkExpectation =
| { kind: "absent" }
| { kind: "valid"; target: string }
export async function removeManagedCollectionLink(
collectionPath: string,View on GitHub (pinned to c9c10f8c75)
Solutions
- Inspect what is there: ls -la ~/.codex/skills/compound-engineering-local.
- If the contents are unwanted or duplicates of the plugin skills, remove them: rm -rf ~/.codex/skills/compound-engineering-local, then re-run the local command.
- If the contents matter, move them aside: mv ~/.codex/skills/compound-engineering-local ~/.codex/skills/compound-engineering-local.bak, then re-run.
- Prefer bun run codex:dev -- remove first — it sweeps known stale install surfaces before re-linking.
Example fix
// before
~/.codex/skills/compound-engineering-local/ // real directory
codex:dev -- local // error: exists and is not a symlink
// after
mv ~/.codex/skills/compound-engineering-local{,.bak}
codex:dev -- local // symlink created Defensive patterns
Strategy: try-catch
Validate before calling
import { lstat } from "node:fs/promises"
import path from "node:path"
const p = path.join(process.env.CODEX_HOME || path.join(process.env.HOME!, ".codex"), "skills", "compound-engineering-local")
try {
const s = await lstat(p)
if (!s.isSymbolicLink()) console.warn(`${p} is a real ${s.isDirectory() ? "directory" : "file"}; move it aside before linking`)
} catch { /* absent: safe to link */ } Try / catch
try {
await activateLocalCollection(ctx)
} catch (error) {
if ((error as Error).message.includes("exists and is not a symlink")) {
console.error(`Move aside or remove ${ctx.collectionPath}, then retry`)
} else throw error
} Prevention
- Never manually copy skill folders into ~/.codex/skills/compound-engineering-local.
- Run bun run codex:dev -- remove before switching install strategies.
- Check ls -la ~/.codex/skills/compound-engineering-local when upgrading from older installs.
When it happens
Trigger: Calling activateLocalCollection (via codex:dev -- local/refresh) when <codexHome>/skills/compound-engineering-local already exists as a real directory or regular file — inspectLocalCollection returns kind "collision" (src/dev/codex-dev.ts:266).
Common situations: A previous manual install copied skills into that directory; an older plugin version installed a real directory there; a backup/restore recreated the path as a plain folder.
Related errors
- ${context.collectionPath} points outside a Compound Engineer
- ${context.collectionPath} is a broken symlink; refusing to o
- ${collectionPath} changed since it was inspected; refusing t
- ${collectionPath} changed since it was inspected; refusing t
- ${context.collectionPath} exists and is not a symlink; refus
AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31).
Data as JSON: /api/errors/cc504e1c78523383.
Report an issue: GitHub.