affaan-m/ECC · error
Refusing to disable modified Claude hooks in
Error message
Refusing to disable modified Claude hooks in ${operation.destinationPath}; run the ECC uninstaller to review retained entries. What it means
During a preflight of Claude settings operations, ECC found managed hooks in the destination settings file that no longer match what ECC originally wrote. Rather than silently disabling user-modified hooks, the installer aborts and directs the user to the ECC uninstaller, which offers an interactive review of each retained entry. This is a safety gate against destroying manual customizations.
Solutions
- Run the ECC uninstaller and review the retained hook entries, choosing which to keep or discard.
- Manually revert your edits to the managed hooks in the settings file so they match the original ECC output, then re-run the install.
- Remove the modified hook entries from the settings file yourself, then re-run the installer.
- Delete/repair the install-state record if it is stale, so previousManagedHooks match disk again.
Example fix
// before (hand-edited settings.json hook command) "command": "node ~/.claude/scripts/hooks/my-custom-hook.js" // after (restore ECC-managed hook or remove it, then rerun installer) "command": "node ~/.claude/scripts/hooks/suggest-compact.js"
Defensive patterns
Strategy: validation
Validate before calling
import { findPreviousManagedHooks, readSettings } from './scripts/lib/install/apply.js';
const settings = readSettings(settingsPath);
const prev = findPreviousManagedHooks(state, plan, op);
if (prev.length !== settingsMatchingHooks(settings).length) {
console.warn('Managed hooks were modified; run the ECC uninstaller first.');
} Try / catch
try {
await previewInstallPlan(plan);
} catch (e) {
if (e.message.includes('Refusing to disable modified Claude hooks')) {
await runUninstallerInteractive(); // review retained entries
} else throw e;
} Prevention
- Never hand-edit hook entries ECC manages in ~/.claude/settings.json.
- Run install in preview mode before applying to catch modified-hook conflicts early.
- Keep install-state files intact so previousManagedHooks can be matched against disk.
When it happens
Trigger: Calling preflightClaudeSettingsOperations (via previewInstallPlan or preparedClaudeSettings) with a 'remove-claude-settings-hooks' operation where uninstallManagedHooks(settings, managedHooks) returns a non-empty retained list — i.e. the hook entries on disk differ from the previously recorded managed hook state.
Common situations: A developer hand-edited ~/.claude/settings.json (changed the hook command, timeout, or matcher) after installing ECC, then re-runs the installer or an uninstall flow that needs to disable those hooks; or the install-state file is stale/missing so ECC cannot match the hooks it wrote.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Refusing to manage Claude hooks outside the canonical…
- Claude settings contains multiple hooks for event
- Invalid : expected hooks. to be an array
- Invalid : expected "hooks" to be a JSON object
- Refusing to overwrite Claude hook for event
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/44e5e5b454e13484.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/apply.js:333
function preflightClaudeSettingsOperations(plan) {
const settingsOperations = plan.operations.filter(operation => (
operation.kind === 'update-claude-settings'
|| operation.kind === 'remove-claude-settings-hooks'
));
if (settingsOperations.length === 0) {
return new Map();
}
const previousState = readPreviousInstallState(plan);
return new Map(settingsOperations.map(operation => {
assertSafeInstallOperation(plan, operation);
const managedHooks = validateManagedHooks(operation.managedHooks);
const settings = readSettings(operation.destinationPath);
const previousManagedHooks = findPreviousManagedHooks(previousState, plan, operation);
if (operation.kind === 'remove-claude-settings-hooks') {
const removal = uninstallManagedHooks(settings, managedHooks);
if (removal.retained.length > 0) {
throw new Error(
`Refusing to disable modified Claude hooks in ${operation.destinationPath}; `
+ 'run the ECC uninstaller to review retained entries.'
);
}
} else {
mergeManagedHooks(settings, managedHooks, { previousManagedHooks });
}
return [operation, { managedHooks, previousManagedHooks }];
}));
}
function prepareHookConsentMigration(plan, migration) {
if (plan.hookConsent !== 'declined') {
return migration;
}
const previousState = readPreviousInstallState(plan);
if (!previousState) {
return migration;View on GitHub (pinned to 8321021c54)