santifer/career-ops · error · Error

plugins/_template/ not found

Error message

plugins/_template/ not found

What it means

Thrown by scaffoldNew() when the source template directory plugins/_template/ does not exist. The scaffolder copies from this shipped template, so its absence means the repo install is incomplete or the working directory is wrong.

Source

Thrown at plugin-install.mjs:121

 */
export function auditRegistryEntry(url, sha, expectId) {
  let parsed;
  try { parsed = parseRepoArg(url); } catch (e) { return [e.message]; }
  if (expectId && parsed.id !== expectId) return [`repo "${url}" → id "${parsed.id}" but registry id is "${expectId}"`];
  let dir;
  try { dir = safeClone(parsed.url, sha); } catch (e) { return [e.message]; }
  let result;
  try { result = validateInstall(dir, parsed.id); }
  catch (e) { rmSync(dir, { recursive: true, force: true }); return [e.message]; }
  try { rmSync(result.dir || dir, { recursive: true, force: true }); } catch { /* best-effort */ }
  return result.ok ? [] : result.problems;
}

/** Scaffold a new local plugin from plugins/_template/. */
export function scaffoldNew(root, name) {
  if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) throw new Error(`plugin name must match [a-z0-9-] (got "${name}")`);
  const tpl = path.join(root, 'plugins', '_template');
  if (!existsSync(tpl)) throw new Error('plugins/_template/ not found');
  const dest = path.join(root, 'plugins.local', name);
  if (existsSync(dest)) throw new Error(`plugins.local/${name} already exists`);
  mkdirSync(path.join(root, 'plugins.local'), { recursive: true });
  cpSync(tpl, dest, { recursive: true });
  // Substitute {{NAME}} placeholders in shipped text files.
  const sub = (p) => { if (existsSync(p)) writeFileSync(p, readFileSync(p, 'utf8').replaceAll('{{NAME}}', name), 'utf8'); };
  for (const f of readdirSync(dest, { withFileTypes: true })) {
    if (f.isFile()) sub(path.join(dest, f.name));
  }
  if (existsSync(path.join(dest, 'test'))) for (const f of readdirSync(path.join(dest, 'test'))) sub(path.join(dest, 'test', f));
  return dest;
}

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Confirm you are running from the repo root where plugins/ lives.
  2. Restore plugins/_template/ from the upstream repo (git checkout origin/main -- plugins/_template).
  3. Re-clone or re-install career-ops if the template is genuinely missing.
  4. Verify with: ls plugins/_template/ before scaffolding.

Example fix

// before: template dir missing -> throws
scaffoldNew(root, 'x');
// after: restore template then scaffold
git checkout origin/main -- plugins/_template
scaffoldNew(root, 'x');
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'fs';
import path from 'path';
function templatePresent(root) {
  return existsSync(path.join(root, 'plugins', '_template'));
}
if (!templatePresent(root)) {
  // restore template from upstream before scaffolding
}

Type guard

null

Try / catch

try {
  scaffoldNew(root, name);
} catch (e) {
  if (/_template\/ not found/.test(e.message)) {
    // restore plugins/_template from upstream, retry
  } else throw e;
}

Prevention

When it happens

Trigger: Running scaffold from a checkout that lacks plugins/_template/ (partial clone, custom slimmed install); running from a cwd where the relative path plugins/_template doesn't resolve; the template was deleted/moved in a fork.

Common situations: Fresh clone interrupted before plugins/ fully populated; running the runner from a subdirectory; a fork removed the template directory.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/27d6b4b47b3cb2bd. Report an issue: GitHub.