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

  1. Inspect what is there: ls -la ~/.codex/skills/compound-engineering-local.
  2. 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.
  3. If the contents matter, move them aside: mv ~/.codex/skills/compound-engineering-local ~/.codex/skills/compound-engineering-local.bak, then re-run.
  4. 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

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


AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31). Data as JSON: /api/errors/cc504e1c78523383. Report an issue: GitHub.