{"record":{"id":"ea2816beb2ddcbf3","repo":"chatboxai/chatbox","slug":"manifest-contains-a-duplicate-path-descriptor-p","errorCode":null,"errorMessage":"Manifest contains a duplicate path: ${descriptor.path}","messagePattern":"Manifest contains a duplicate path: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/packages/backup/import-backup.ts","lineNumber":90,"sourceCode":"  }\n}\n\nfunction parseJson(bytes: Uint8Array, path: string): unknown {\n  if (bytes.length > MAX_BACKUP_JSON_ENTRY_BYTES) throw new Error(`Backup JSON entry is too large: ${path}`)\n  return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes)) as unknown\n}\n\nfunction validateManifestEntries(manifest: BackupManifest, stagedEntries: Map<string, StagedEntry>) {\n  const descriptors = [\n    manifest.data.settings,\n    manifest.data.copilots,\n    manifest.data.sessionSettings,\n    ...manifest.sessions,\n    ...manifest.resources,\n  ].filter((entry): entry is NonNullable<typeof entry> => Boolean(entry))\n  const expectedPaths = new Set<string>([BACKUP_MANIFEST_PATH])\n  for (const descriptor of descriptors) {\n    if (expectedPaths.has(descriptor.path)) throw new Error(`Manifest contains a duplicate path: ${descriptor.path}`)\n    expectedPaths.add(descriptor.path)\n    const staged = stagedEntries.get(descriptor.path)\n    if (!staged) throw new Error(`Backup entry is missing: ${descriptor.path}`)\n    if (staged.size !== descriptor.size) throw new Error(`Backup entry size mismatch: ${descriptor.path}`)\n    if (staged.checksum.value !== descriptor.checksum.value) {\n      throw new Error(`Backup entry checksum mismatch: ${descriptor.path}`)\n    }\n  }\n  for (const path of stagedEntries.keys()) {\n    if (!expectedPaths.has(path)) throw new Error(`Backup contains an entry not listed in manifest: ${path}`)\n  }\n  if (expectedPaths.size !== stagedEntries.size) throw new Error('Backup manifest entry list is incomplete')\n  validateBackupManifestGraph(manifest)\n}\n\nasync function readStagedResource(storage: BackupStorage, plan: Pick<ResourceWritePlan, 'resource' | 'tempKey'>) {\n  const base64 = await storage.getBlob(plan.tempKey)\n  if (base64 === null) throw new Error(`Staged resource is missing: ${plan.resource.path}`)","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/backup/import-backup.ts#L72-L108","documentation":"Thrown by validateManifestEntries when two manifest descriptors (settings, copilots, sessionSettings, a session, or a resource) share the same archive path. Each entry must occupy a unique path so staging and checksum verification are unambiguous. Propagates out of importBackupArchive after rollback.","triggerScenarios":"A manifest where, for example, two sessions derive the same sessions/<id>/session.json path (collision after safeSessionPathComponent sha256 fallback), or a resource path collides with another descriptor's path.","commonSituations":"Two sessions whose ids hash to the same sha256 prefix producing duplicate session.json paths; a hand-crafted or tampered manifest; a bug in path generation during export.","solutions":["Regenerate the backup so the exporter assigns unique paths (safeSessionPathComponent already dedupes via sha256; a real collision points to duplicate source ids or an encoder bug).","If the manifest was hand-edited, restore the original or fix the duplicate path.","For two colliding session ids, rename one in the source data before re-export."],"exampleFix":"// before: importing a manifest with two descriptors at the same path\nawait importBackupArchive(file, options)\n// after: detect duplicate descriptor paths before importing\nconst paths = descriptors.map((d) => d.path)\nif (new Set(paths).size !== paths.length) {\n  throw new Error('Manifest has duplicate paths: ' + paths.filter((p, i) => paths.indexOf(p) !== i))\n}","handlingStrategy":"validation","validationCode":"// Before import, fail fast on duplicate descriptor paths in the manifest.\nconst paths = [\n  manifest.data.settings?.path,\n  manifest.data.copilots?.path,\n  manifest.data.sessionSettings?.path,\n  ...manifest.sessions.map((s) => s.path),\n  ...manifest.resources.map((r) => r.path),\n].filter(Boolean) as string[]\nif (new Set(paths).size !== paths.length) {\n  throw new Error('Manifest declares duplicate paths')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await importBackupArchive(file, options)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Manifest contains a duplicate path')) {\n    // Regenerate the backup; do not attempt to merge paths by hand.\n    throw new Error('Backup manifest is inconsistent; re-create it', { cause: error })\n  }\n  throw error\n}","preventionTips":["Trust the exporter's safeSessionPathComponent to assign unique paths; do not hand-edit manifests.","Avoid duplicate session ids in source data before exporting.","Validate manifest path uniqueness immediately after parsing the manifest, before staging."],"tags":["backup","import","manifest","duplicate","path","validation"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}