{"record":{"id":"e0c3eff5f8426032","repo":"EveryInc/compound-engineering-plugin","slug":"context-collectionpath-exists-and-is-not-a-syml-e0c3ef","errorCode":null,"errorMessage":"${context.collectionPath} exists and is not a symlink; refusing to remove it","messagePattern":"(.+?) exists and is not a symlink; refusing to remove it","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/dev/codex-dev.ts","lineNumber":422,"sourceCode":"  context: CodexDevContext,\n  previous: Extract<LocalCollectionState, { kind: \"absent\" | \"valid\" }>,\n  activatedTarget: string,\n): Promise<void> {\n  if (previous.kind === \"absent\") {\n    await removeManagedCollectionLink(context.collectionPath, activatedTarget, { ignoreChanges: true })\n    return\n  }\n  await replaceManagedCollectionLink(context.collectionPath, previous.target, {\n    kind: \"valid\",\n    target: activatedTarget,\n  })\n}\n\nexport async function removeLocalCollection(context: CodexDevContext): Promise<boolean> {\n  const state = await inspectLocalCollection(context)\n  if (state.kind === \"absent\") return false\n  if (state.kind === \"collision\") {\n    throw new Error(`${context.collectionPath} exists and is not a symlink; refusing to remove it`)\n  }\n  if (state.kind === \"unrelated\") {\n    throw new Error(`${context.collectionPath} points outside a Compound Engineering checkout; refusing to remove it`)\n  }\n  if (state.kind === \"broken\") {\n    throw new Error(`${context.collectionPath} is a broken symlink; refusing to remove it automatically`)\n  }\n  await removeManagedCollectionLink(context.collectionPath, state.target)\n  return true\n}\n\nfunction parseJson<T>(result: CommandResult, label: string): T {\n  try {\n    return JSON.parse(result.stdout) as T\n  } catch (error) {\n    throw new Error(`${label} returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`)\n  }\n}","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/EveryInc/compound-engineering-plugin/blob/c9c10f8c75412c7232cb2bd663e5fd1cea98d84e/src/dev/codex-dev.ts#L404-L440","documentation":"removeLocalCollection() removes the symlinked local skills collection but only if it manages that entry. A 'collision' state means the path exists as a regular file or real directory (not a symlink), so the code refuses to delete user data it did not create.","triggerScenarios":"Calling removeLocalCollection() (directly or via switchToRemote() / removeCodexDevInstallation()) when collectionPath is a plain directory or file rather than the managed symlink — inspectLocalCollection returns kind='collision'.","commonSituations":"The user previously copied real skill files into the collection path instead of symlinking; a plugin manager materialized the directory; an earlier failed run replaced the symlink with a real directory.","solutions":["Inspect the path: `ls -la <collectionPath>` to confirm it is a directory/file you created yourself","Back up anything valuable inside it before touching it","Remove it manually (`rm -rf <collectionPath>` or `rm <collectionPath>`) once confirmed safe","Re-run the codex:dev command, which will then manage a fresh symlink"],"exampleFix":"# before\n$ bun run codex:dev -- remote\n# Error: ... exists and is not a symlink; refusing to remove it\n\n# after\n$ ls -la /path/to/CODEX_HOME/skills/compound-engineering-local  # verify it is yours\n$ mv /path/to/CODEX_HOME/skills/compound-engineering-local /tmp/ce-backup\n$ bun run codex:dev -- remote","handlingStrategy":"validation","validationCode":"const state = await inspectLocalCollection(context);\nif (state.kind === \"collision\") {\n  const stat = await fs.lstat(context.collectionPath);\n  console.warn(`${context.collectionPath} is a real ${stat.isDirectory() ? \"directory\" : \"file\"}; move it aside before removing`);\n  return;\n}","typeGuard":"function isSafeToRemove(s: { kind: string }): s is { kind: \"absent\" | \"valid\" | \"unrelated\" | \"broken\" } {\n  return s.kind !== \"collision\";\n}","tryCatchPattern":"try {\n  await removeLocalCollection(context);\n} catch (e) {\n  if (String(e).includes(\"is not a symlink\")) {\n    await fs.rename(context.collectionPath, context.collectionPath + \".bak\"); // preserve user data\n    await removeLocalCollection(context);\n  } else throw e;\n}","preventionTips":["Never hand-place real files at the collection path; always let codex:dev create the symlink","Check `ls -la <collectionPath>` before switching modes","Only delete non-symlink content you created and backed up"],"tags":["filesystem","safety-guard","symlink","codex-dev"],"backgroundTag":"refusing-to-overwrite-existing-path","analyzedSha":"c9c10f8c75412c7232cb2bd663e5fd1cea98d84e","analyzedAt":"2026-08-31T15:18:07.959Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}