{"record":{"id":"68e5200b8eef02de","repo":"chatboxai/chatbox","slug":"backup-entry-is-missing-descriptor-path","errorCode":null,"errorMessage":"Backup entry is missing: ${descriptor.path}","messagePattern":"Backup entry is missing: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/packages/backup/import-backup.ts","lineNumber":93,"sourceCode":"function 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}`)\n  return decodeStoredBlob(base64ToBytes(base64), plan.resource.encoding, plan.resource.mimeType)\n}\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/backup/import-backup.ts#L75-L111","documentation":"Thrown when a manifest descriptor references an archive path that has no matching staged entry - the zip did not contain that file. The manifest is the source of truth; every listed entry must physically exist in the archive. Propagates after rollback.","triggerScenarios":"manifest.sessions, manifest.resources, or manifest.data.* lists a path, but stagedEntries has no entry for it because the zip stream lacked that file.","commonSituations":"Interrupted download/copy of the .backup file; a file removed from the zip after creation; a manifest and zip taken from mismatched export runs.","solutions":["Re-download or re-create the backup file from its source.","Verify archive integrity (unzip listing) before import.","If the source is unrecoverable and you accept data loss, edit the manifest to remove the dangling descriptor."],"exampleFix":"// before: archive missing files the manifest references\nawait importBackupArchive(file, options)\n// after: confirm every manifest path is present in the zip first\nconst present = new Set(zipEntryPaths)\nconst missing = descriptors.map((d) => d.path).filter((p) => !present.has(p))\nif (missing.length) throw new Error('Archive is missing: ' + missing.join(', '))","handlingStrategy":"validation","validationCode":"// Confirm every manifest descriptor path is present in the archive listing.\nconst listing = await listZipEntries(file)\nconst present = new Set(listing)\nconst required = descriptors.map((d) => d.path)\nconst absent = required.filter((p) => !present.has(p))\nif (absent.length) throw new Error('Archive is incomplete; missing: ' + absent.join(', '))","typeGuard":null,"tryCatchPattern":"try {\n  await importBackupArchive(file, options)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Backup entry is missing')) {\n    // Re-download or re-create the backup file.\n    throw new Error('Backup file is incomplete', { cause: error })\n  }\n  throw error\n}","preventionTips":["Verify the backup file size/checksum against the source after downloading.","Do not interrupt exports; a truncated zip produces exactly this mismatch.","List the zip contents before import as a cheap integrity check."],"tags":["backup","import","manifest","missing-entry","integrity"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}