affaan-m/ECC · error · Error
Invalid ECC repo root: unreadable package.json at ${packageJ
Error message
Invalid ECC repo root: unreadable package.json at ${packageJsonPath} What it means
The scope-migration tool moves the ECC Claude plugin from one scope (user/project/local) to another, then runs verifyFinalState() to re-read the plugin inventory and confirm the plugin now exists ONLY in the destination scope. This error (code FINAL_VERIFICATION_FAILED) fires when that post-migration inventory still shows a source-scope copy, shows the plugin in both scopes, or shows it missing from the destination — i.e. the migration did not converge to a clean destination-only state. It carries structured recovery commands.
Source
Thrown at scripts/auto-update.js:147
function validateRepoRoot(repoRoot) {
const normalized = path.resolve(repoRoot);
const packageJsonPath = path.join(normalized, 'package.json');
const installApplyPath = path.join(normalized, 'scripts', 'install-apply.js');
if (!fs.existsSync(packageJsonPath)) {
throw new Error(`Invalid ECC repo root: missing package.json at ${packageJsonPath}`);
}
if (!fs.existsSync(installApplyPath)) {
throw new Error(`Invalid ECC repo root: missing install script at ${installApplyPath}`);
}
let pkgName = null;
try {
pkgName = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).name;
} catch {
throw new Error(`Invalid ECC repo root: unreadable package.json at ${packageJsonPath}`);
}
if (!ECC_PACKAGE_NAMES.has(pkgName)) {
throw new Error(`Refusing to run install from untrusted repo root ${normalized}: package.json name '${pkgName}' is not an official ECC package.`);
}
return normalized;
}
function runExternalCommand(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: options.cwd,
env: options.env || process.env,
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024
});
if (result.error) {
throw result.error;View on GitHub (pinned to 01e15490f0)
Solutions
- Run the structured recovery commands embedded in the error object (err.recovery / recoveryCommands(null, destinationScope)) — typically a `claude plugin uninstall` of the source scope followed by an install in the destination.
- Manually open the relevant settings.json (user: ~/.claude/settings.json, project: .claude/settings.json, local) and remove the stale source-scope ECC plugin entry, leaving only the destination one.
- Delete or refresh any stale local plugin inventory cache, then re-run the migration.
- Re-run migrateClaudePluginScope with the same scope once the conflicting entry is removed.
Example fix
// before
await migrateClaudePluginScope({ scope: 'project' }); // throws FINAL_VERIFICATION_FAILED
// after — handle structured recovery
try {
await migrateClaudePluginScope({ scope: 'project' });
} catch (err) {
if (err.code === 'FINAL_VERIFICATION_FAILED') {
for (const cmd of err.recovery) console.log('Run:', cmd);
process.exit(1);
}
throw err;
} Defensive patterns
Strategy: try-catch
Type guard
// Detect a migration error by its structured code field.
function isFinalVerificationError(e) {
return e && e.code === 'FINAL_VERIFICATION_FAILED';
} Try / catch
try {
await migrateClaudePluginScope({ scope: 'project' });
} catch (err) {
if (err.code === 'FINAL_VERIFICATION_FAILED') {
// err.recovery holds the exact uninstall/install commands to run.
console.error('Migration did not converge. Run these commands:');
for (const cmd of (err.recovery || [])) console.error(' ' + cmd);
process.exit(1);
}
throw err;
} Prevention
- Do not hand-edit settings.json plugin entries while a migration is in progress.
- Remove stale local plugin inventory caches before re-running a failed migration.
- Always run the recovery commands in the structured error before retrying.
When it happens
Trigger: Running migrateClaudePluginScope({ scope: 'project' }) after the destination install succeeded but the source-scope uninstall failed silently; manual edits to settings.json leaving duplicate enabled entries; an interrupted migration that installed the destination but never removed the source; a conflicting/stale local plugin inventory file.
Common situations: A previous migration was interrupted; settings.json was hand-edited so the plugin appears in two scopes; a stale ~/.claude/plugins local inventory disagrees with the real settings; Claude CLI reported a successful uninstall but the entry persisted.
Related errors
- ECC_PROJECT_DIR must be a child path within /workspace.
- ${command} ${args.join(' ')} failed${errorOutput ? `: ${erro
- The canonical ito-compute-cli is unpublished and ECC will no
- Invalid mode "${mode}". Allowed modes: ${allowedModes.join('
- Claude Code command contains characters that are unsafe for
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/79d3c01e282c3174.
Report an issue: GitHub.