affaan-m/ECC · error
Metadata reorder detected at
Error message
Metadata reorder detected at ${location}; move the matching sidecar entry before refreshing fingerprints What it means
withRefreshedFingerprints() in scripts/lib/hooks-config.js cross-checks the order of hook entries in hooks.json against fingerprint positions recorded in the metadata sidecar. If an entry's fingerprint exists in the sidecar but at a different location (event[index]) than its current position, it throws a 'Metadata reorder detected' error. This is an integrity guard: fingerprints are position-sensitive, so a silent reorder would attach metadata (matchers, descriptions) to the wrong hook.
Solutions
- Reorder the entries in the metadata sidecar to mirror the new order in hooks.json (the error names the exact location, e.g. Stop[2]).
- Alternatively revert hooks.json to the order recorded in the sidecar, then re-apply changes via the ECC tooling (addJsonMergeOperation) so sidecar and hooks stay in sync.
- If the reorder is intentional and metadata mapping is stale, regenerate the sidecar metadata for the new order rather than refreshing fingerprints in place.
Example fix
// before: hooks.json has matcher A,B but sidecar lists fingerprints as [B,A] withRefreshedFingerprints(...); // throws Metadata reorder detected at Stop[1] // after: reorder sidecar entries array to [fingerprintA, fingerprintB] metadata.entries.Stop = [fpA, fpB]; // match hooks.json order withRefreshedFingerprints(...); // ok
Defensive patterns
Strategy: validation
Validate before calling
// verify hooks.json entry order matches sidecar metadata before refreshing
const cfg = JSON.parse(fs.readFileSync(hooksPath, 'utf8'));
const meta = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
for (const [event, entries] of Object.entries(cfg.hooks || {})) {
const fps = (meta.entries?.[event] || []).map(e => e.fingerprint);
const current = entries.map(e => fingerprintFor(e));
const aligned = current.every((fp, i) => fp === fps[i]);
if (!aligned) throw new Error(`Reorder ${event} sidecar entries before refreshing fingerprints`);
} Type guard
function isAlignedOrder(entries, metadataEntries) {
return Array.isArray(entries) && Array.isArray(metadataEntries) &&
entries.length === metadataEntries.length;
} Try / catch
try {
const result = withRefreshedFingerprints(/* ... */);
} catch (err) {
if (String(err.message).startsWith('Metadata reorder detected')) {
const loc = err.message.match(/at (\S+);/)?.[1];
console.error(`Reorder sidecar entry for ${loc} to match hooks.json, then retry.`);
} else throw err;
} Prevention
- Never reorder hook arrays by hand — use the ECC tooling that updates the sidecar in lockstep.
- When reordering is needed, move entries in both hooks.json and the metadata sidecar in the same edit.
- Disable formatters/plugins that sort array entries inside hooks.json.
- Treat hooks.json + sidecar as an atomic pair in backups and branch merges.
When it happens
Trigger: Calling validateHooks() or refreshed() after manually reordering, inserting, or deleting entries within a hooks event array in hooks.json without moving the corresponding entries in the metadata sidecar (.hooks-metadata style file) to match.
Common situations: Hand-editing hooks.json to reorder matchers; a formatter or merge that reshuffled array entries; deleting an earlier hook so subsequent indexes shift while sidecar entries keep old indexes.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- does not line up with :\n
- artifact lacks genre binding
- Claude settings contains multiple hooks for event
- --enable-hooks and --no-hooks are mutually exclusive
- failure.reason
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/218c29f35e53c02d.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/hooks-config.js:248
// A known fingerprint at another position signals a reorder, not a command
// edit. Require the author to move its metadata before refreshing anything.
const positions = new Map();
for (const [event, entries] of Object.entries(events)) {
if (!Array.isArray(entries)) continue;
entries.forEach((entry, index) => {
const fingerprint = fingerprintHookEntry(entry);
const locations = positions.get(fingerprint) || [];
positions.set(fingerprint, [...locations, `${event}[${index}]`]);
});
}
for (const [event, entries] of Object.entries(entriesByEvent)) {
if (!Array.isArray(entries)) continue;
entries.forEach((entry, index) => {
const locations = positions.get(entry?.fingerprint);
const location = `${event}[${index}]`;
if (locations && !locations.includes(location)) {
throw new Error(`Metadata reorder detected at ${location}; move the matching sidecar entry before refreshing fingerprints`);
}
});
}
for (const [event, eventMetadata] of Object.entries(entriesByEvent)) {
const entries = Array.isArray(events[event]) ? events[event] : [];
refreshed[event] = Array.isArray(eventMetadata)
? eventMetadata.map((entry, index) => (
entry && typeof entry === 'object' && index < entries.length
? { ...entry, fingerprint: fingerprintHookEntry(entries[index]) }
: entry
))
: eventMetadata;
}
return { ...metadata, entries: refreshed };
}
View on GitHub (pinned to 8321021c54)