santifer/career-ops · error · Error
plugin rejected: - ${result.problems.join('\n - ')}
Error message
plugin rejected:
- ${result.problems.join('\n - ')} What it means
Thrown by installFromRepo() when validateInstall() returns ok:false, collecting every static problem (missing required files manifest.json/index.mjs/README.md/LICENSE, invalid manifest JSON, manifest id mismatch, or audit findings). The cloned tree and its temp dir are cleaned up, and the problems are listed in one combined error.
Source
Thrown at plugin-install.mjs:89
return { ok: true, problems: [], manifest, dir };
}
/**
* Install a community plugin from a github repo at a pinned SHA into
* plugins.local/<id>. Returns { id, manifest, integrity, dir } WITHOUT enabling
* it (the caller runs the consent gate). Throws on any validation failure.
*/
export function installFromRepo(root, { url, sha }) {
const { url: safeUrl, id } = parseRepoArg(url);
const dest = path.join(root, 'plugins.local', id);
if (existsSync(dest)) throw new Error(`plugins.local/${id} already exists — \`node plugins.mjs remove ${id}\` first`);
let cloned = safeClone(safeUrl, sha);
let result;
try { result = validateInstall(cloned, id); }
catch (e) { rmSync(cloned, { recursive: true, force: true }); throw e; }
if (!result.ok) {
rmSync(result.dir || cloned, { recursive: true, force: true });
throw new Error(`plugin rejected:\n - ${result.problems.join('\n - ')}`);
}
mkdirSync(path.join(root, 'plugins.local'), { recursive: true });
cpSync(result.dir, dest, { recursive: true });
rmSync(result.dir, { recursive: true, force: true });
const tree = hashPluginTree(dest);
return { id, manifest: { ...result.manifest, dir: dest }, integrity: tree.integrity, files: tree.files, repo: safeUrl, sha };
}
/**
* Clone + statically validate a registry entry WITHOUT installing it (used by
* the registry-validate CI). Executes NO plugin code — manifest is parsed, the
* audit is static. Returns problems (empty = clean).
* @returns {string[]}
*/
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}"`];View on GitHub (pinned to 9b17a8ac97)
Solutions
- Read every line under 'plugin rejected:' — each names a concrete defect.
- Fix the defects in the plugin repo at the pinned SHA (or pick a corrected SHA).
- Ensure manifest.json/index.mjs/README.md/LICENSE all exist and the manifest id matches the repo name suffix.
- Re-run `node plugins.mjs install <repo> --sha <new-sha>` after fixes.
Example fix
// before: repo missing LICENSE -> ['missing required file: LICENSE'] // after: add LICENSE at the plugin repo, pin a new SHA, reinstall
Defensive patterns
Strategy: validation
Validate before calling
// pre-flight the plugin tree before attempting install
const REQUIRED = ['manifest.json', 'index.mjs', 'README.md', 'LICENSE'];
function hasMinFiles(treeDir) {
return REQUIRED.every(f => existsSync(path.join(treeDir, f)));
} Type guard
null
Try / catch
try {
installFromRepo(root, { url, sha });
} catch (e) {
if (/plugin rejected/.test(e.message)) {
// parse the bullet list, surface each defect to the user
} else throw e;
} Prevention
- Inspect the plugin repo for the four required files before pinning a SHA.
- Ensure the manifest id matches the repo name suffix.
- Review audit findings before retrying; don't widen permissions to silence them.
When it happens
Trigger: The cloned plugin is missing one of the four MIN_FILES; manifest.json is not valid JSON; the manifest's id does not match the repo-name id; plugin-audit.mjs flagged a static issue (e.g. disallowed API surface, suspicious code).
Common situations: Installing an incomplete/WIP plugin repo; manifest id drift between repo name and manifest field; the plugin author omitted LICENSE or README; the audit caught a risky pattern.
Related errors
- repo must be named "career-ops-plugin-<name>" (got "${repoNa
- refusing non-GitHub/unsafe repo URL: ${arg} (expected https:
- a 40-hex commit --sha is required (got ${JSON.stringify(sha)
- clone of ${url}@${sha.slice(0, 10)} failed — ${err.stderr ?
- plugin name must match [a-z0-9-] (got "${name}")
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/fabb8308c80f1ac8.
Report an issue: GitHub.