{"record":{"id":"b0e3dc153447712e","repo":"chatboxai/chatbox","slug":"backup-archive-was-not-fully-generated","errorCode":null,"errorMessage":"Backup archive was not fully generated","messagePattern":"Backup archive was not fully generated","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/packages/backup/export-backup.ts","lineNumber":408,"sourceCode":"      stats: {\n        sessionCount: sessions.length,\n        resourceCount: resources.length,\n        deduplicatedResourceCount: Math.max(0, successfullyReadResourceKeys - resources.length),\n        warningCount: warnings.length,\n      },\n    })\n    validateBackupManifestGraph(manifest)\n    completedManifest = manifest\n    options.onProgress?.({ phase: 'packing', current: 0, total: 1 })\n    const manifestEntry = await jsonEntry(BACKUP_MANIFEST_PATH, completedManifest)\n    yield manifestEntry.archive\n    options.onProgress?.({ phase: 'packing', current: 1, total: 1 })\n  }\n\n  const writeResult = await options.writeArchive(() =>\n    createZipStream(enforceArchiveLimits(generateEntries()), options.signal)\n  )\n  if (!completedManifest) throw new Error('Backup archive was not fully generated')\n  return {\n    manifest: completedManifest,\n    boundedMemory: writeResult.boundedMemory,\n    pendingDownload: writeResult.pendingDownload,\n  }\n}\n","sourceCodeStart":390,"sourceCodeEnd":415,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/backup/export-backup.ts#L390-L415","documentation":"Thrown by exportBackupArchive after writeArchive returns if completedManifest is still undefined. completedManifest is only assigned at the end of the generateEntries async generator (line 398, after the manifest entry is yielded at line 401), so if the generator never fully ran the manifest is missing. Unlike the in-loop throws this one propagates to the caller and aborts the export.","triggerScenarios":"options.writeArchive never invoked its dataCallback; the dataCallback did not fully iterate the returned async generator (stopped after a bounded number of chunks); or it swallowed an error thrown inside the generator instead of propagating it, leaving completedManifest unset.","commonSituations":"A custom writeArchive that breaks out of the for-await loop early (e.g. streamed only the first chunk to disk); a writeArchive that catches and discards generator errors; a generator that stopped before yielding the manifest because of an earlier swallowed throw.","solutions":["Make writeArchive fully drain the async generator it receives before resolving its promise.","Do not catch errors inside writeArchive without rethrowing; let them propagate so exportBackupArchive fails loudly.","If writeArchive must bound memory, stream every chunk to the sink instead of stopping early.","Add a test asserting writeArchive consumes the generator to completion."],"exampleFix":"// before: stops after the first chunk, manifest never emitted\nconst writeArchive = async (dataCallback) => {\n  for await (const chunk of dataCallback()) { sink.write(chunk); break }\n  return { boundedMemory: 0, pendingDownload: Promise.resolve() }\n}\n// after: drain the whole generator before resolving\nconst writeArchive = async (dataCallback) => {\n  let boundedMemory = 0\n  for await (const chunk of dataCallback()) { sink.write(chunk); boundedMemory++ }\n  return { boundedMemory, pendingDownload: Promise.resolve() }\n}","handlingStrategy":"try-catch","validationCode":"// Assert your writeArchive drains the generator to completion before wiring it in.\nconst assertDrains = async (writeArchive: BackupExportOptions['writeArchive']) => {\n  let count = 0\n  await writeArchive(async function* () { for (let i = 0; i < 5; i++) { yield new Uint8Array(); count++ } })\n  if (count !== 5) throw new Error('writeArchive does not drain the generator')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await exportBackupArchive(options)\n} catch (error) {\n  if (error instanceof Error && error.message === 'Backup archive was not fully generated') {\n    // writeArchive did not consume the generator to the manifest entry.\n    throw new Error('Export failed: archive writer stopped early', { cause: error })\n  }\n  throw error\n}","preventionTips":["In writeArchive, always `for await` the generator to completion before resolving.","Never break/return out of the writeArchive loop early, even on a chunk-size heuristic.","Do not swallow errors raised inside the generator; rethrow them so this guard is never the only signal."],"tags":["backup","export","archive","generator","control-flow"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}