affaan-m/ECC · error

Invalid : expected globally unique id

Error message

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

What it means

Hook entry ids must be globally unique across all events in the managed hooks map, because ids are the lookup key ECC uses to identify its managed hooks in settings.json. validateManagedHooks throws this when a second entry reuses an id already seen earlier in the same map (regardless of event).

Solutions

  1. Rename the duplicate entry's id to a new unique value.
  2. Use the quoted id in the message to search the hooks map and locate both occurrences.
  3. If the same hook is genuinely intended on two events, clone it with a distinct id per event (e.g. 'fmt-pre', 'fmt-post').
  4. Generate ids programmatically (event + purpose suffix) to guarantee uniqueness.

Example fix

// before
{ PreToolUse: [{ id: 'fmt', ... }], PostToolUse: [{ id: 'fmt', ... }] }
// after
{ PreToolUse: [{ id: 'fmt-pre', ... }], PostToolUse: [{ id: 'fmt-post', ... }] }
Defensive patterns

Strategy: validation

Validate before calling

const seen = new Set();
for (const entries of Object.values(managedHooks)) {
  for (const e of entries) {
    if (seen.has(e.id)) throw new Error(`Duplicate hook id: ${e.id}`);
    seen.add(e.id);
  }
}

Type guard

function hasUniqueIds(hooks) {
  const ids = Object.values(hooks).flat().map(e => e?.id);
  return ids.length === new Set(ids).size;
}

Try / catch

try {
  expectedHooks(hooks);
} catch (err) {
  if (/globally unique id/.test(err.message)) {
    const dup = err.message.match(/"([^"]+)"/)?.[1];
    console.error(`Rename one of the entries with id "${dup}"`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Duplicating an entry (copy/paste) with the same id under the same or a different event, e.g. two entries both with id: 'fmt' under PreToolUse and PostToolUse.

Common situations: Copy-pasting an entry to add it to another event without renaming the id; generating ids with a function that always returns the same value; merging two hook packs that coincidentally use the same ids.

Related errors


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

Appendix: source

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

    }
    if (entries.length === 0) {
      throw new Error(`Invalid ${label}.${event}: expected at least one hook entry`);
    }

    entries.forEach((entry, index) => {
      if (!isJsonObject(entry)) {
        throw new Error(
          `Invalid managed hook entry at ${label}.${event}[${index}]: expected a JSON object`
        );
      }
      if (typeof entry.id !== 'string' || entry.id.trim() === '') {
        throw new Error(
          `Invalid managed hook entry at ${label}.${event}[${index}]: `
          + 'expected a non-empty unique id'
        );
      }
      if (seenIds.has(entry.id)) {
        throw new Error(`Invalid ${label}: expected globally unique id "${entry.id}"`);
      }
      seenIds.add(entry.id);
      if (
        !Object.prototype.hasOwnProperty.call(entry, 'matcher')
        && !EVENTS_WITHOUT_MATCHER.has(event)
      ) {
        throw new Error(
          `Invalid managed hook entry at ${label}.${event}[${index}]: missing matcher`
        );
      }
      if (
        Object.prototype.hasOwnProperty.call(entry, 'matcher')
        && typeof entry.matcher !== 'string'
        && !isJsonObject(entry.matcher)
      ) {
        throw new Error(
          `Invalid managed hook entry at ${label}.${event}[${index}]: invalid matcher`
        );

View on GitHub (pinned to 8321021c54)