{"record":{"id":"79d3c01e282c3174","repo":"affaan-m/ECC","slug":"invalid-ecc-repo-root-unreadable-package-json-at","errorCode":null,"errorMessage":"Invalid ECC repo root: unreadable package.json at ${packageJsonPath}","messagePattern":"Invalid ECC repo root: unreadable package\\.json at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/auto-update.js","lineNumber":147,"sourceCode":"\nfunction validateRepoRoot(repoRoot) {\n  const normalized = path.resolve(repoRoot);\n  const packageJsonPath = path.join(normalized, 'package.json');\n  const installApplyPath = path.join(normalized, 'scripts', 'install-apply.js');\n\n  if (!fs.existsSync(packageJsonPath)) {\n    throw new Error(`Invalid ECC repo root: missing package.json at ${packageJsonPath}`);\n  }\n\n  if (!fs.existsSync(installApplyPath)) {\n    throw new Error(`Invalid ECC repo root: missing install script at ${installApplyPath}`);\n  }\n\n  let pkgName = null;\n  try {\n    pkgName = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).name;\n  } catch {\n    throw new Error(`Invalid ECC repo root: unreadable package.json at ${packageJsonPath}`);\n  }\n  if (!ECC_PACKAGE_NAMES.has(pkgName)) {\n    throw new Error(`Refusing to run install from untrusted repo root ${normalized}: package.json name '${pkgName}' is not an official ECC package.`);\n  }\n\n  return normalized;\n}\n\nfunction runExternalCommand(command, args, options = {}) {\n  const result = spawnSync(command, args, {\n    cwd: options.cwd,\n    env: options.env || process.env,\n    encoding: 'utf8',\n    maxBuffer: 10 * 1024 * 1024\n  });\n\n  if (result.error) {\n    throw result.error;","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/auto-update.js#L129-L165","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait migrateClaudePluginScope({ scope: 'project' }); // throws FINAL_VERIFICATION_FAILED\n\n// after — handle structured recovery\ntry {\n  await migrateClaudePluginScope({ scope: 'project' });\n} catch (err) {\n  if (err.code === 'FINAL_VERIFICATION_FAILED') {\n    for (const cmd of err.recovery) console.log('Run:', cmd);\n    process.exit(1);\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"// Detect a migration error by its structured code field.\nfunction isFinalVerificationError(e) {\n  return e && e.code === 'FINAL_VERIFICATION_FAILED';\n}","tryCatchPattern":"try {\n  await migrateClaudePluginScope({ scope: 'project' });\n} catch (err) {\n  if (err.code === 'FINAL_VERIFICATION_FAILED') {\n    // err.recovery holds the exact uninstall/install commands to run.\n    console.error('Migration did not converge. Run these commands:');\n    for (const cmd of (err.recovery || [])) console.error('  ' + cmd);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["migration","claude-plugin","scope","configuration","fail-closed"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}