{"record":{"id":"abfdd55acdd3e66c","repo":"chatboxai/chatbox","slug":"managed-resource-is-missing-from-blob-storage","errorCode":null,"errorMessage":"Managed resource is missing from blob storage","messagePattern":"Managed resource is missing from blob storage","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/renderer/packages/backup/export-backup.ts","lineNumber":301,"sourceCode":"          })\n        }\n      }\n      options.onProgress?.({ phase: 'sessions', current: sessionIds.length, total: sessionIds.length })\n    }\n\n    const resourceCandidateEntries = Array.from(resourceCandidates.entries())\n    for (let index = 0; index < resourceCandidateEntries.length; index++) {\n      throwIfAborted(options.signal)\n      const [storageKey, candidate] = resourceCandidateEntries[index]\n      options.onProgress?.({\n        phase: 'resources',\n        current: index,\n        total: resourceCandidateEntries.length,\n        label: candidate.references.find((reference) => reference.filename)?.filename,\n      })\n      try {\n        const storedValue = await options.storage.getBlob(storageKey)\n        if (storedValue === null) throw new Error('Managed resource is missing from blob storage')\n        const firstReference = candidate.references[0]\n        const encoded = encodeStoredBlob(storedValue, firstReference)\n        if (encoded.bytes.length > MAX_BACKUP_RESOURCE_ENTRY_BYTES) {\n          throw new Error('Managed resource exceeds the backup size limit')\n        }\n        const checksum = await sha256Checksum(encoded.bytes)\n        successfullyReadResourceKeys++\n        const dedupeKey = `${checksum.value}:${encoded.encoding}:${encoded.mimeType}`\n        const existingResource = deduplicatedResources.get(dedupeKey)\n        const sessionIds = unique(\n          candidate.references.flatMap((reference) => (reference.sessionId ? [reference.sessionId] : []))\n        )\n        if (existingResource) {\n          existingResource.originalStorageKeys.push(storageKey)\n          existingResource.sessionIds = unique([...existingResource.sessionIds, ...sessionIds])\n          const candidateScope = resolveResourceScope(candidate)\n          if (\n            existingResource.scope !== candidateScope ||","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/backup/export-backup.ts#L283-L319","documentation":"Thrown in the export resource loop when storage.getBlob(storageKey) returns null for a key that a session or global settings referenced. The reference was collected via collectSessionResourceReferences/collectGlobalResourceReferences, so the exporter expects a blob to exist; absence means an orphaned reference. Caught in the same loop (lines 352-359) and downgraded to a 'resource-read-failed' warning, so export proceeds without that resource.","triggerScenarios":"A picture:/file:/link:/parseFile-/parseUrl- storage key is referenced by a message, attachment, avatar, background, or settings field, but getBlob returns null for it during the resource collection phase.","commonSituations":"A resource was deleted without cleaning up message references; external-file mode where the blob lives outside managed storage; partial cleanup after a failed import; storage backend lost blobs due to quota eviction or an IndexedDB clear.","solutions":["Run a referential-integrity sweep that removes or ignores references whose blobs are missing before exporting.","If using external file storage, configure collectSessionResourceReferences to skip external resources (producing an 'external-resource-skipped' warning) instead of treating them as managed blobs.","Re-import or re-attach the missing blobs into managed storage.","Exclude the conversations containing the orphaned references from exportItems."],"exampleFix":"// before: missing blobs warn on every export\nawait exportBackupArchive(options)\n// after: drop references to blobs that no longer exist\nfor (const key of await storage.getAllKeys()) {\n  if (!key.startsWith('picture:') && !key.startsWith('file:')) continue\n  if ((await storage.getBlob(key)) === null) {\n    // strip the reference from owning sessions/settings before export\n  }\n}\nawait exportBackupArchive(options)","handlingStrategy":"validation","validationCode":"// Before export, confirm every referenced blob actually exists.\nconst referenced = collectAllResourceReferences(storage, settings, copilots, sessions)\nconst missing: string[] = []\nfor (const key of referenced) {\n  if ((await storage.getBlob(key)) === null) missing.push(key)\n}\nif (missing.length) console.warn('Orphaned resource refs:', missing)","typeGuard":"// True when the blob backing a reference is present and non-null.\nconst blobIsPresent = async (storage: BackupStorage, key: string): Promise<boolean> =>\n  (await storage.getBlob(key)) !== null","tryCatchPattern":null,"preventionTips":["Always delete a managed blob in the same operation that removes its reference from a message/settings.","For external-file mode, configure resource collection to skip external references rather than treat them as managed.","Surface 'resource-read-failed' warnings from the result to users so they can re-attach missing files."],"tags":["backup","export","resource","blob-storage","orphaned-reference","data-integrity"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}