{"record":{"id":"f552a45f304e7f0c","repo":"linshenkx/prompt-optimizer","slug":"error-storage-read","errorCode":"error.storage.read","errorMessage":"[ImageMultiImageSession] Missing input image asset: ${assetId}","messagePattern":"\\[ImageMultiImageSession\\] Missing input image asset: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/src/stores/session/useImageMultiImageSession.ts","lineNumber":670,"sourceCode":"    if (!saved) return\n\n    const parsed =\n      typeof saved === 'string'\n        ? (JSON.parse(saved) as Record<string, unknown>)\n        : (saved as Record<string, unknown>)\n\n    const rawImages = Array.isArray(parsed.inputImages) ? parsed.inputImages : []\n    const restoredImages = await Promise.all(\n      rawImages.map(async (item) => {\n        const record = (item || {}) as Record<string, unknown>\n        const assetId = typeof record.assetId === 'string' ? record.assetId : null\n        let mimeType = typeof record.mimeType === 'string' ? record.mimeType : 'image/png'\n        let b64 = typeof record.b64 === 'string' ? record.b64 : ''\n\n        if (!b64 && assetId) {\n          const fullImage = await imageStorageService.getImage(assetId)\n          if (!fullImage?.data?.trim()) {\n            throw createMissingInputAssetError(assetId)\n          }\n\n          b64 = fullImage.data\n          mimeType = fullImage.metadata?.mimeType || mimeType\n        }\n\n        if (!b64.trim()) {\n          throw new Error('[ImageMultiImageSession] Missing input image base64 data in persisted session')\n        }\n\n        return {\n          id: typeof record.id === 'string' ? record.id : createRuntimeImageId(),\n          assetId,\n          b64,\n          mimeType,\n        } satisfies MultiImageSessionInputItem\n      }),\n    )","sourceCodeStart":652,"sourceCodeEnd":688,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/ui/src/stores/session/useImageMultiImageSession.ts#L652-L688","documentation":"In the image multi-image session store, when a generated image record lacks inline base64 data and an assetId is present, the store fetches the full image from imageStorageService.getImage(assetId). If the returned record is missing or its data is empty/whitespace, createMissingInputAssetError (code error.storage.read) is thrown, flagging a lost input image asset.","triggerScenarios":"A session step references an assetId that was pruned from image storage (cache eviction, storage quota cleanup, IndexedDB corruption), or getImage resolves undefined/empty for that ID — e.g. after clearing browser storage while the session persisted.","commonSituations":"User clears site data / private-mode storage limits delete the image blob but the session document keeps the assetId; cross-tab writes racing; storage schema migrations that dropped old assets; assets written to a different storage backend than the one read.","solutions":["Check whether the asset still exists in image storage for that assetId and inspect how/when it was written (correct DB/store, successful write awaited).","Catch error.storage.read failures per-asset and degrade gracefully (skip/regenerate the image) instead of failing the whole session step.","Persist images with the session record (inline b64) or make eviction of assets also update session records, so references never dangle."],"exampleFix":"// before\nconst fullImage = await imageStorageService.getImage(assetId)\nif (!fullImage?.data?.trim()) {\n  throw createMissingInputAssetError(assetId)\n}\n// after (degrade instead of abort)\nconst fullImage = await imageStorageService.getImage(assetId).catch(() => undefined)\nif (!fullImage?.data?.trim()) {\n  console.warn('[ImageMultiImageSession] missing asset, skipping:', assetId)\n  return null\n}","handlingStrategy":"fallback","validationCode":"const exists = await imageStorageService.getImage(assetId).then(r => Boolean(r?.data?.trim())).catch(() => false)\nif (!exists) {\n  // skip / regenerate the image instead of proceeding\n}","typeGuard":"const hasImageData = (img: Awaited<ReturnType<typeof imageStorageService.getImage>>): img is NonNullable<typeof img> & { data: string } =>\n  Boolean(img && img.data && img.data.trim().length > 0)","tryCatchPattern":"try { b64 = (await imageStorageService.getImage(assetId))?.data ?? '' } catch (e) {\n  if ((e as { code?: string }).code === 'error.storage.read') { /* log & degrade: skip or regenerate */ }\n  else throw e\n}","preventionTips":["Persist base64 data inline with session records for critical inputs.","Never evict image assets without also updating sessions that reference them.","Wrap per-asset reads in try/catch and degrade gracefully instead of failing the whole step.","Test storage-migration paths so old assetIds remain resolvable."],"tags":["storage","images","session","indexeddb","data-loss"],"backgroundTag":"missing-storage-asset","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}