affaan-m/ECC · error

Invalid : expected unique id " " within event

Error message

Invalid ${label}: expected unique id "${entry.id}" within event "${event}"

What it means

Within a single hook event, each recorded entry must have a unique id. The validator tracks seen ids per event and throws when a duplicate appears, preventing ambiguous hook identity that would break deduplication and idempotent installs of managed hooks into Claude settings.

Solutions

  1. Rename one of the duplicate ids so each entry within the event is unique
  2. Deduplicate entries before validation (keep the newest/first by id)
  3. Regenerate the recording after fixing the source hooks.json
  4. If merging recordings programmatically, key entries by id and overwrite instead of concatenating

Example fix

// before
[ { "id": "fmt", ... }, { "id": "fmt", ... } ]
// after
[ { "id": "fmt", ... }, { "id": "fmt-v2", ... } ]
Defensive patterns

Strategy: validation

Validate before calling

function hasUniqueIds(entries) { const s = new Set(); return entries.every(e => !s.has(e.id) && s.add(e.id)); }

Type guard

null

Try / catch

try { validateRecordedManagedHooks(hooks); } catch (e) { if (e.message.includes('expected unique id')) { hooks = dedupeById(hooks); validateRecordedManagedHooks(hooks); } else throw e; }

Prevention

When it happens

Trigger: Calling validateRecordedManagedHooks (or its callers validate/previousHooks/recordedHooks) where two entries under the same event key share the same id string.

Common situations: Merging two hooks.json sources that both use the default id, copy-pasting an entry block without changing its id, or a plugin upgrade appending its hooks again onto existing settings.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/cd7230a19fed68c2. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/install/claude-settings.js:208

  return cloneValue(managedHooks);
}

function validateRecordedManagedHooks(managedHooks, label = 'recorded managed hooks') {
  if (!isJsonObject(managedHooks) || Object.keys(managedHooks).length === 0) {
    throw new Error(`Invalid ${label}: expected a non-empty JSON object`);
  }
  for (const [event, entries] of Object.entries(managedHooks)) {
    if (!isNonEmptyString(event) || !Array.isArray(entries) || entries.length === 0) {
      throw new Error(`Invalid ${label}.${event}: expected a non-empty hook array`);
    }
    const seenIds = new Set();
    entries.forEach((entry, index) => {
      if (!isJsonObject(entry) || !isNonEmptyString(entry.id) || !Array.isArray(entry.hooks)) {
        throw new Error(`Invalid hook entry at ${label}.${event}[${index}]`);
      }
      if (seenIds.has(entry.id)) {
        throw new Error(
          `Invalid ${label}: expected unique id "${entry.id}" within event "${event}"`
        );
      }
      seenIds.add(entry.id);
    });
  }
  return cloneValue(managedHooks);
}

function validateSettings(settings, label = 'Claude settings') {
  if (!isJsonObject(settings)) {
    throw new Error(`Invalid ${label}: expected a JSON object`);
  }

  if (Object.prototype.hasOwnProperty.call(settings, 'hooks')) {
    if (!isJsonObject(settings.hooks)) {
      throw new Error(`Invalid ${label}: expected "hooks" to be a JSON object`);
    }

View on GitHub (pinned to 8321021c54)