affaan-m/ECC · error · Error
Memory ${memoryId} is duplicated in ${matches.length} files.
Error message
Memory ${memoryId} is duplicated in ${matches.length} files. What it means
readMemoryById requires that an id maps to exactly one file. If two or more files in the scanned scopes share the same memory id (after applying the targetHarness filter), the vault refuses to pick one arbitrarily and throws a duplication error. This protects against ambiguous reads when the vault integrity invariant (one id -> one file) has been broken.
Source
Thrown at scripts/lib/memory-vault.js:673
function readMemoryById(id, options = {}) {
const memoryId = validateMemoryId(id);
const targetHarness = options.targetHarness
? validateSlug(options.targetHarness, 'target harness')
: null;
const loaded = readMemoryFiles(options);
const matches = loaded.entries
.filter(entry => entry.memory.id === memoryId)
.filter(entry => (
!targetHarness
|| entry.memory.targetHarnesses.includes('all')
|| entry.memory.targetHarnesses.includes(targetHarness)
));
if (matches.length === 0) {
throw new Error(`Memory ${memoryId} was not found.`);
}
if (matches.length > 1) {
throw new Error(`Memory ${memoryId} is duplicated in ${matches.length} files.`);
}
const allBacklinks = loaded.entries
.filter(entry => entry.memory.links.includes(memoryId))
.filter(entry => entry.memory.status === 'active')
.map(entry => entry.memory)
.filter(memory => (
!targetHarness
|| memory.targetHarnesses.includes('all')
|| memory.targetHarnesses.includes(targetHarness)
))
.sort((left, right) => left.id.localeCompare(right.id));
const backlinks = allBacklinks
.slice(0, MAX_RESULTS)
.map(summarizeMemory);
return {
...matches[0],
backlinks,
backlinksTruncated: allBacklinks.length > backlinks.length,View on GitHub (pinned to 01e15490f0)
Solutions
- Run doctorMemoryVault to list every duplicate id and the offending file paths.
- Decide which copy is canonical, delete the others (rm <path>), and re-run readMemoryById.
- If both copies have unique content, re-save the non-canonical one with a new id (load it, saveMemory with a fresh id and a link to the survivor), then delete the original duplicate.
- Fix the upstream cause: stop syncing the vault with a file-level sync tool, or fix the idFactory that produced the collision.
Example fix
// diagnose
const report = doctorMemoryVault({});
console.log(report.duplicateIds); // [{ id: 'mem_x', paths: ['project:notes/mem_x.md', 'user:notes/mem_x.md'] }]
// fix: remove the non-canonical copy
fs.unlinkSync('<user-root>/notes/mem_x.md');
const mem = readMemoryById('mem_x'); // now resolves uniquely Defensive patterns
Strategy: validation
Validate before calling
const { doctorMemoryVault } = require('./scripts/lib/memory-vault');
function assertNoDuplicateIds(options = {}) {
const report = doctorMemoryVault(options);
if (report.duplicateIdCount > 0) {
throw new Error(`Duplicate memory ids detected: ${report.duplicateIds.map(d => d.id).join(', ')}`);
}
}
// before readMemoryById:
assertNoDuplicateIds(options); Try / catch
try { return readMemoryById(id, options); }
catch (error) {
if (/is duplicated in/.test(error.message)) {
const report = doctorMemoryVault(options);
console.error('Resolve duplicates:', report.duplicateIds);
return null;
}
throw error;
} Prevention
- Run doctorMemoryVault periodically to catch duplicate ids early.
- Do not file-level sync the vault with tools that create conflict copies.
- When merging vaults, copy memories through saveMemory (which regenerates ids) rather than copying files.
- Never copy a memory .md file by hand without changing its id.
When it happens
Trigger: Two .md files under the scanned vault roots parse to memories with identical id fields. Caused by a buggy save that wrote the same id twice, a manual copy/paste of a memory file, a botched vault merge, or a sync conflict that created a duplicate. doctorMemoryVault reports the same condition in its duplicateIds diagnostic.
Common situations: Sync conflicts (Syncthing/Dropbox) creating mem_x.md and mem_x (1).md both with the same id; manually copying a memory file to a second scope without changing the id; a race in a custom idFactory; merging two users' vaults by copying files.
Related errors
- ${label} must be a non-empty string.
- ${label} is too long (maximum ${maxChars} characters).
- ${label} must not contain control or bidirectional formattin
- ${label} must be one of: ${allowed.join(', ')}.
- ${label} must be a lowercase letters/numbers slug.
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/27dfbc57dd30f8b6.
Report an issue: GitHub.