EveryInc/compound-engineering-plugin · error · Error

Cannot switch to local mode while the local collection is

Error message

Cannot switch to local mode while the local collection is ${previous.kind}

What it means

switchToLocal() only proceeds when the collection path is 'absent' or already a 'valid' managed link. Any other state (collision, unrelated, broken) is rejected up front with this error, so the caller fixes the path state before activation can be attempted.

Solutions

  1. Run `bun run codex:dev -- status` to see the reported state
  2. For 'collision': back up and remove the non-symlink directory/file at collectionPath yourself, then retry
  3. For 'broken': delete the dangling symlink (`rm <collectionPath>`) or restore the missing checkout, then retry
  4. For 'unrelated': re-point or remove the foreign symlink once confirmed it is not needed, then retry

Example fix

# before (dangling link from a deleted checkout)
$ bun run codex:dev -- local
// Error: Cannot switch to local mode while the local collection is broken

# after
$ rm /path/to/CODEX_HOME/skills/compound-engineering-local
$ bun run codex:dev -- local   # now state is 'absent' and activation proceeds
Defensive patterns

Strategy: validation

Validate before calling

const state = await inspectLocalCollection(context);
if (state.kind !== "absent" && state.kind !== "valid") {
  // remediate first: rm dangling/foreign links, move aside real dirs
  console.warn(`collection is ${state.kind}; fix before switching to local`);
} else {
  await switchToLocal(context);
}

Type guard

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

Try / catch

try {
  await switchToLocal(context);
} catch (e) {
  if (String(e).includes("Cannot switch to local mode")) {
    const kind = String(e).match(/collection is (\w+)/)?.[1];
    console.error(`remediate state '${kind}' (see status), then retry`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling switchToLocal() (via runCodexDevCommand or status-driven flows) when inspectLocalCollection returns kind='collision' (real file/dir), 'unrelated' (symlink outside a CE checkout), or 'broken' (dangling symlink).

Common situations: A real skills directory or another plugin's symlink already occupies the collection path; the previous local checkout was deleted leaving a dangling link; a hand-made symlink to the wrong repository.

Related errors


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

Appendix: source

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

    plugins.some((plugin) => plugin.pluginId === OFFICIAL_PLUGIN_ID) ||
    (await isOfficialPluginConfigured(context))
  ) {
    await runCodex(context, runner, ["plugin", "remove", OFFICIAL_PLUGIN_ID, "--json"])
  }
  await removePlugins(
    context,
    runner,
    plugins.filter((plugin) => plugin.pluginId !== OFFICIAL_PLUGIN_ID),
  )
}

export async function switchToLocal(
  context: CodexDevContext,
  runner: CommandRunner = new BunCommandRunner(),
): Promise<CodexDevStatus> {
  const previous = await inspectLocalCollection(context)
  if (previous.kind !== "absent" && previous.kind !== "valid") {
    throw new Error(`Cannot switch to local mode while the local collection is ${previous.kind}`)
  }
  const activatedTarget = await fs.realpath(context.skillsRoot)
  await activateLocalCollection(context)
  try {
    const plugins = await listCompoundEngineeringPlugins(context, runner)
    await removeLocalPluginConflicts(context, runner, plugins)
    const status = await inspectCodexDevStatus(context, runner)
    if (status.mode !== "local") {
      throw new Error(`Could not verify local Codex development mode (reported ${status.mode})`)
    }
    return status
  } catch (error) {
    await restoreLocalCollection(context, previous, activatedTarget)
    throw error
  }
}

async function listMarketplaces(context: CodexDevContext, runner: CommandRunner): Promise<Marketplace[]> {

View on GitHub (pinned to c9c10f8c75)