{"record":{"id":"a0331adfd1dbff7d","repo":"chatboxai/chatbox","slug":"backup-manifest-entry-list-is-incomplete","errorCode":null,"errorMessage":"Backup manifest entry list is incomplete","messagePattern":"Backup manifest entry list is incomplete","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/packages/backup/import-backup.ts","lineNumber":102,"sourceCode":"    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}`)\n  return decodeStoredBlob(base64ToBytes(base64), plan.resource.encoding, plan.resource.mimeType)\n}\n\nasync function findAvailableCollisionKey(storage: BackupStorage, originalKey: string, reserved: Set<string>) {\n  for (let attempt = 0; attempt < 100; attempt++) {\n    let candidatePrefix = 'resource:imported'\n    for (const prefix of ['picture:', 'file:', 'link:', 'parseFile-', 'parseUrl-']) {\n      if (originalKey.startsWith(prefix)) {\n        candidatePrefix = `${prefix}imported`\n        break\n      }\n    }","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/backup/import-backup.ts#L84-L120","documentation":"Thrown as a final invariant in validateManifestEntries when the number of expected paths does not equal the number of staged entries. The preceding loops already reject duplicate descriptor paths (125), missing staged entries (126), and unexpected staged paths (128), so a size mismatch here indicates an internal logic error rather than bad archive data. Propagates after rollback.","triggerScenarios":"expectedPaths.size !== stagedEntries.size after the duplicate/missing/unexpected checks all passed - only reachable through a bug in descriptor construction or set bookkeeping.","commonSituations":"Essentially unreachable with a well-formed archive; indicates a regression in validateManifestEntries, a manifest schema that permits duplicate-but-distinct descriptors, or concurrent mutation of stagedEntries during validation.","solutions":["Treat as a code defect: audit validateManifestEntries and the descriptor list construction for missing/duplicate handling.","Ensure stagedEntries and the manifest descriptors are not mutated concurrently during validation.","Add a unit test with a known-good archive to confirm the invariant holds.","Report with the full manifest if it reproduces."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// No data-level pre-check applies; the invariant should be unreachable. In tests, assert it\n// never fires for a corpus of well-formed archives.\nfor (const archive of validArchives) {\n  const staged = await stage(archive)\n  validateManifestEntries(parseManifest(archive), staged) // must not throw\n}","typeGuard":null,"tryCatchPattern":"try {\n  await importBackupArchive(file, options)\n} catch (error) {\n  if (error instanceof Error && error.message === 'Backup manifest entry list is incomplete') {\n    // Treat as a bug in validateManifestEntries or a concurrent-mutation issue.\n    throw new Error('Internal invariant violated; report this archive', { cause: error })\n  }\n  throw error\n}","preventionTips":["Do not mutate the stagedEntries map or manifest concurrently with import.","Add regression tests covering duplicate, missing, and extra entries so this guard stays unreachable.","If it ever fires, preserve the archive and manifest for a code-level audit."],"tags":["backup","import","manifest","invariant","defensive","unreachable"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}