EveryInc/compound-engineering-plugin · error · Error

${context.collectionPath} points outside a Compound Engineer

Error message

${context.collectionPath} points outside a Compound Engineering checkout; refusing to remove it

What it means

removeLocalCollection() refuses to delete a symlink that resolves to a skills directory outside any Compound Engineering checkout ('unrelated' state). This prevents the tool from destroying a symlink owned by a different project or plugin that merely occupies the collection path.

Source

Thrown at src/dev/codex-dev.ts:425

): Promise<void> {
  if (previous.kind === "absent") {
    await removeManagedCollectionLink(context.collectionPath, activatedTarget, { ignoreChanges: true })
    return
  }
  await replaceManagedCollectionLink(context.collectionPath, previous.target, {
    kind: "valid",
    target: activatedTarget,
  })
}

export async function removeLocalCollection(context: CodexDevContext): Promise<boolean> {
  const state = await inspectLocalCollection(context)
  if (state.kind === "absent") return false
  if (state.kind === "collision") {
    throw new Error(`${context.collectionPath} exists and is not a symlink; refusing to remove it`)
  }
  if (state.kind === "unrelated") {
    throw new Error(`${context.collectionPath} points outside a Compound Engineering checkout; refusing to remove it`)
  }
  if (state.kind === "broken") {
    throw new Error(`${context.collectionPath} is a broken symlink; refusing to remove it automatically`)
  }
  await removeManagedCollectionLink(context.collectionPath, state.target)
  return true
}

function parseJson<T>(result: CommandResult, label: string): T {
  try {
    return JSON.parse(result.stdout) as T
  } catch (error) {
    throw new Error(`${label} returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`)
  }
}

async function runCodex(
  context: CodexDevContext,

View on GitHub (pinned to c9c10f8c75)

Solutions

  1. Check the link target: `readlink <collectionPath>` and `realpath <collectionPath>`
  2. If it is another tool's symlink, remove it manually with `rm <collectionPath>` after confirming
  3. If it should be a Compound Engineering checkout, restore the missing repo marker files or point it at the correct checkout, then re-run
  4. Use `bun run codex:dev -- status` to see the resolved state before deciding

Example fix

# before
$ bun run codex:dev -- remote
# Error: ... points outside a Compound Engineering checkout; refusing to remove it

# after
$ readlink /path/to/CODEX_HOME/skills/compound-engineering-local
$ rm /path/to/CODEX_HOME/skills/compound-engineering-local  # verified as another tool's link
$ bun run codex:dev -- remote
Defensive patterns

Strategy: validation

Validate before calling

const state = await inspectLocalCollection(context);
if (state.kind === "unrelated") {
  console.log(`link targets ${state.resolvedTarget}; verify ownership before removing manually`);
  return;
}

Type guard

function isManagedLink(s: { kind: string }): boolean {
  return s.kind === "absent" || s.kind === "valid" || s.kind === "broken";
}

Try / catch

try {
  await removeLocalCollection(context);
} catch (e) {
  if (String(e).includes("points outside")) {
    console.error("foreign symlink: inspect with readlink and remove manually only if it is yours");
  } else throw e;
}

Prevention

When it happens

Trigger: collectionPath is a symlink whose realpath does not pass assertCompoundEngineeringRepo(), or its basename is not 'skills' — e.g. it points at another repo's directory — and removeLocalCollection() (or switchToRemote / removeCodexDevInstallation) is called.

Common situations: The symlink was retargeted by hand or by another tool to a different checkout; the target repo's marker files were removed so it no longer registers as a Compound Engineering checkout; multiple plugins competing for the same skills path.

Related errors


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