{"record":{"id":"59ee6673b3b26872","repo":"santifer/career-ops","slug":"plugins-local-name-already-exists","errorCode":null,"errorMessage":"plugins.local/${name} already exists","messagePattern":"plugins\\.local/(.+?) already exists","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugin-install.mjs","lineNumber":123,"sourceCode":"  let parsed;\n  try { parsed = parseRepoArg(url); } catch (e) { return [e.message]; }\n  if (expectId && parsed.id !== expectId) return [`repo \"${url}\" → id \"${parsed.id}\" but registry id is \"${expectId}\"`];\n  let dir;\n  try { dir = safeClone(parsed.url, sha); } catch (e) { return [e.message]; }\n  let result;\n  try { result = validateInstall(dir, parsed.id); }\n  catch (e) { rmSync(dir, { recursive: true, force: true }); return [e.message]; }\n  try { rmSync(result.dir || dir, { recursive: true, force: true }); } catch { /* best-effort */ }\n  return result.ok ? [] : result.problems;\n}\n\n/** Scaffold a new local plugin from plugins/_template/. */\nexport function scaffoldNew(root, name) {\n  if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) throw new Error(`plugin name must match [a-z0-9-] (got \"${name}\")`);\n  const tpl = path.join(root, 'plugins', '_template');\n  if (!existsSync(tpl)) throw new Error('plugins/_template/ not found');\n  const dest = path.join(root, 'plugins.local', name);\n  if (existsSync(dest)) throw new Error(`plugins.local/${name} already exists`);\n  mkdirSync(path.join(root, 'plugins.local'), { recursive: true });\n  cpSync(tpl, dest, { recursive: true });\n  // Substitute {{NAME}} placeholders in shipped text files.\n  const sub = (p) => { if (existsSync(p)) writeFileSync(p, readFileSync(p, 'utf8').replaceAll('{{NAME}}', name), 'utf8'); };\n  for (const f of readdirSync(dest, { withFileTypes: true })) {\n    if (f.isFile()) sub(path.join(dest, f.name));\n  }\n  if (existsSync(path.join(dest, 'test'))) for (const f of readdirSync(path.join(dest, 'test'))) sub(path.join(dest, 'test', f));\n  return dest;\n}\n","sourceCodeStart":105,"sourceCodeEnd":134,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/plugin-install.mjs#L105-L134","documentation":"Thrown by scaffoldNew() when the destination plugins.local/<name> already exists. Mirrors the install path's guard: scaffolding over an existing plugin would clobber user edits silently, so it refuses and asks for an explicit remove first.","triggerScenarios":"Scaffolding a name that was already scaffolded/installed; a prior scaffold left a directory; name collision with an installed community plugin of the same id.","commonSituations":"Re-running scaffold during iteration; scaffolding a name already taken by an installed plugin; a previous interrupted scaffold left a partial dir.","solutions":["Run `node plugins.mjs remove <name>` to delete the existing dir, then scaffold again.","Choose a different name if you want to keep the existing one.","Manually rm -rf plugins.local/<name> if it is a known leftover.","Confirm the name is not already in use via `node plugins.mjs list`."],"exampleFix":"// before: scaffold over existing\ncpSync(tpl, dest); // throws: already exists\n// after: remove then scaffold\n// `node plugins.mjs remove my-plugin`\n// `node plugins.mjs scaffold my-plugin`","handlingStrategy":"validation","validationCode":"import { existsSync } from 'fs';\nimport path from 'path';\nfunction scaffoldDestFree(root, name) {\n  return !existsSync(path.join(root, 'plugins.local', name));\n}\nif (!scaffoldDestFree(root, name)) {\n  // remove existing plugin or choose a new name\n}","typeGuard":"null","tryCatchPattern":"try {\n  scaffoldNew(root, name);\n} catch (e) {\n  if (/already exists/.test(e.message)) {\n    // `node plugins.mjs remove <name>` then retry\n  } else throw e;\n}","preventionTips":["Check `node plugins.mjs list` to avoid name collisions before scaffolding.","Remove an existing plugin before re-scaffolding the same name.","Pick unique slugs for each new plugin."],"tags":["plugins","scaffold","filesystem","idempotency"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}