{"record":{"id":"26b7637b02ea46da","repo":"linshenkx/prompt-optimizer","slug":"failed-to-decode-image-data-url","errorCode":null,"errorMessage":"Failed to decode image data URL","messagePattern":"Failed to decode image data URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/ui/src/composables/app/useAppPromptGardenImport.ts","lineNumber":766,"sourceCode":"  }\n\n  if (typeof FileReader === 'undefined') {\n    throw new Error('FileReader is not available to decode images')\n  }\n\n  const blob = await resp.blob()\n  const actualMime = blob.type || mimeType\n  const dataUrl = await new Promise<string>((resolve, reject) => {\n    const reader = new FileReader()\n    reader.onerror = () => reject(new Error('Failed to read image blob'))\n    reader.onload = () => resolve(String(reader.result || ''))\n    reader.readAsDataURL(blob)\n  })\n\n  const match = dataUrl.match(/^data:.*?;base64,(.*)$/u)\n  const b64 = match ? match[1] : ''\n  if (!b64) {\n    throw new Error('Failed to decode image data URL')\n  }\n  return { b64, mimeType: actualMime || 'application/octet-stream' }\n}\n\nconst dedupeStrings = (items: string[]): string[] => {\n  return Array.from(new Set(items.filter(Boolean)))\n}\n\nconst buildAssetSourceMetadata = (snapshot: GardenSnapshot): { prompt?: string } => {\n  if (snapshot.prompt.format !== 'text') {\n    return {}\n  }\n\n  const prompt = typeof snapshot.prompt.text === 'string' ? snapshot.prompt.text.trim() : ''\n  if (!prompt) return {}\n  return { prompt }\n}\n","sourceCodeStart":748,"sourceCodeEnd":784,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/ui/src/composables/app/useAppPromptGardenImport.ts#L748-L784","documentation":"After fetching image bytes, the blob is read via FileReader.readAsDataURL and the resulting data URL must match /^data:.*?;base64,(.*)$/u. If no base64 segment is captured (b64 is empty), the decode is considered failed. This is a browser-side encoding anomaly, not an HTTP failure.","triggerScenarios":"readAsDataURL produced a data URL without a base64 payload — e.g. an empty or tiny blob, a non-standard mime yielding 'data:,', or browser/encoding edge cases (unusual content-types, blob closed prematurely) that yield a data URL not matching the regex.","commonSituations":"Zero-byte response bodies with 200 status; content-type headers that make the browser emit a plain (non-base64) data URL; unusual mime types from object storage; race where the blob handle was revoked before reading.","solutions":["Log the actual dataUrl string to see what readAsDataURL produced; check for 'data:,' or an empty payload","Verify content-type of the image response is a normal image mime (image/png, image/jpeg); fix the server's header if wrong","Check the response body length (content-length) to rule out empty 200 responses","As a fallback, convert via arrayBuffer + btoa instead of FileReader for deterministic base64 encoding"],"exampleFix":"// before\nreader.readAsDataURL(blob)\nconst b64 = dataUrl.match(/^data:.*?;base64,(.*)$/u)?.[1] ?? ''\n\n// after\nconst buf = new Uint8Array(await blob.arrayBuffer())\nlet bin = ''\nbuf.forEach(b => bin += String.fromCharCode(b))\nconst b64 = btoa(bin)","handlingStrategy":"fallback","validationCode":"if (blob.size === 0) throw new Error('Empty image blob')\nif (!/^image\\//.test(blob.type)) console.warn('Unexpected image mime:', blob.type)","typeGuard":"const isBase64DataUrl = (u: string): boolean => /^data:.*?;base64,.+/u.test(u)","tryCatchPattern":"try { return decodeViaFileReader(blob) } catch { return decodeViaArrayBuffer(blob) /* btoa fallback */ }","preventionTips":["Prefer blob.arrayBuffer() + btoa over FileReader for deterministic base64","Reject empty blobs before reading","Ensure image responses carry correct image/* content-type headers"],"tags":["image","base64","browser-api","file-reader","prompt-garden"],"backgroundTag":"base64-encoding-failed","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}