{"record":{"id":"33acce66c0274f0e","repo":"can1357/oh-my-pi","slug":"import-all-duplicate-id-id-in-the-imported-bat","errorCode":null,"errorMessage":"import_all: duplicate id ${id} in the imported batch. Deduplicate the input before calling.","messagePattern":"import_all: duplicate id (.+?) in the imported batch\\. Deduplicate the input before calling\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/mnemopi/src/core/annotations.ts","lineNumber":339,"sourceCode":"\t\tusing statement = this.db.prepare(\n\t\t\t\"SELECT id, memory_id, kind, value, source, confidence, created_at FROM annotations ORDER BY id\",\n\t\t);\n\t\tconst rows = statement.all() as AnnotationRow[];\n\t\treturn rows.map(normalizeRow);\n\t}\n\timportAll(annotations: readonly AnnotationInput[], force = false): AnnotationImportStats {\n\t\tconst stats: AnnotationImportStats = {\n\t\t\tinserted: 0,\n\t\t\tskipped: 0,\n\t\t\toverwritten: 0,\n\t\t\timported_renumbered: 0,\n\t\t};\n\t\tconst seenIds = new Set<number>();\n\t\tfor (const item of annotations) {\n\t\t\tconst id = rowId(item.id);\n\t\t\tif (id === null) continue;\n\t\t\tif (seenIds.has(id)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`import_all: duplicate id ${id} in the imported batch. Deduplicate the input before calling.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tseenIds.add(id);\n\t\t}\n\n\t\ttransaction(this.db, () => {\n\t\t\tusing existingStatement = this.db.prepare(\n\t\t\t\t\"SELECT id, memory_id, kind, value, source, confidence, created_at FROM annotations\",\n\t\t\t);\n\t\t\tconst existingRows = existingStatement.all() as AnnotationRow[];\n\t\t\tconst existing = new Map<number, StoredAnnotationContent>();\n\t\t\tfor (const row of existingRows) existing.set(Number(row.id), normalizeRow(row));\n\n\t\t\tusing insertWithId = this.db.prepare(\n\t\t\t\t\"INSERT INTO annotations (id, memory_id, kind, value, source, confidence, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n\t\t\t) as WritableStatement;\n\t\t\tusing insertWithoutId = this.db.prepare(","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/mnemopi/src/core/annotations.ts#L321-L357","documentation":"`importAll()` in annotations.ts rejects an imported batch that contains the same annotation id more than once. The bulk import API requires the caller to pre-deduplicate; duplicate ids would make the upsert semantics of the batch ambiguous, so it fails fast with this error instead of silently overwriting.","triggerScenarios":"Calling `annotations.importAll(items)` where two or more items in `items` carry the same `id` (after `rowId()` normalization). Items with null/unset ids are skipped and do not trigger this.","commonSituations":"Merging exported annotation files that overlap, re-importing a batch that already includes previously generated ids, concatenating two exports without dedupe, a producer that assigns ids non-uniquely.","solutions":["Deduplicate the input array by id before calling `importAll` (keep the first or the newest item).","Strip or regenerate ids for items you intend to create fresh so the batch has unique or absent ids.","If merging exports, drop duplicate ids from the older export.","Wrap the call in try-catch and on this error, dedupe and retry once."],"exampleFix":"// before\nawait annotations.importAll([...exportA, ...exportB]);\n// after\nconst seen = new Set<number>();\nconst deduped = [...exportB, ...exportA].filter(it => {\n  const id = it.id;\n  if (id == null) return true;\n  if (seen.has(id)) return false;\n  seen.add(id);\n  return true;\n});\nawait annotations.importAll(deduped);","handlingStrategy":"validation","validationCode":"const ids = annotations.map(a => a.id).filter(id => id != null);\nif (new Set(ids).size !== ids.length) {\n  throw new Error(\"Batch contains duplicate ids; dedupe before importAll\");\n}","typeGuard":"null","tryCatchPattern":"try {\n  await annotations.importAll(batch);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"duplicate id\")) {\n    const seen = new Set();\n    batch = batch.filter(a => a.id == null || !seen.has(a.id) && seen.add(a.id));\n    await annotations.importAll(batch);\n  } else throw err;\n}","preventionTips":["Dedupe merged exports by id before import.","Regenerate ids for items intended as new rows.","Keep a single producer of ids in your pipeline.","Unit-test merge/import helpers with overlapping fixtures."],"tags":["duplicate-id","import","validation"],"backgroundTag":"duplicate-id-in-batch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}