{"record":{"id":"13584dd2d2480bfb","repo":"nexu-io/open-design","slug":"slide-index-1-is-not-a-base64-png-jpeg-data-u","errorCode":null,"errorMessage":"slide ${index + 1} is not a base64 PNG/JPEG data URL","messagePattern":"slide (.+?) is not a base64 PNG/JPEG data URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/deck-export.ts","lineNumber":121,"sourceCode":" * base64 image bytes through the JSON IPC channel for large images.\n */\nexport async function readSlideFiles(paths: string[]): Promise<SlideImage[]> {\n  return Promise.all(\n    paths.map(async (filePath, index) => {\n      if (typeof filePath !== 'string' || filePath.length === 0) {\n        throw new Error(`slide ${index + 1} has no file path`);\n      }\n      const buffer = await readFile(filePath);\n      return { buffer, jpeg: /\\.jpe?g$/i.test(filePath) };\n    }),\n  );\n}\n\nexport function decodeSlideDataUrls(urls: string[]): SlideImage[] {\n  return urls.map((url, index) => {\n    const match = /^data:image\\/(png|jpeg);base64,([A-Za-z0-9+/=]+)$/.exec(url ?? '');\n    if (!match) {\n      throw new Error(`slide ${index + 1} is not a base64 PNG/JPEG data URL`);\n    }\n    return { buffer: Buffer.from(match[2] ?? '', 'base64'), jpeg: match[1] === 'jpeg' };\n  });\n}\n\n// Standard PowerPoint 16:9 slide is 13.333\" x 7.5\". We keep 13.333\" as the slide\n// width and derive the height from the deck's actual aspect ratio, so a 4:3,\n// square, or portrait deck gets a correctly-proportioned slide instead of being\n// letterboxed into a 16:9 frame.\nconst PPTX_SLIDE_WIDTH_IN = 13.333;\n\n/**\n * Assembles per-slide images into a screenshot-based .pptx — one full-bleed\n * image per slide. The slide aspect ratio follows the deck's authored size\n * (`opts.aspect` = width/height); falls back to 16:9. The slides are\n * pixel-perfect images (not editable text), the \"exactly what you see\" export\n * mode. Returns the .pptx bytes.\n */","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/deck-export.ts#L103-L139","documentation":"Thrown by decodeSlideDataUrls() (the base64 data-URL variant of slide ingestion) when one entry does not match the strict regex /^data:image/(png|jpeg);base64,[A-Za-z0-9+/=]+$/. Only PNG and JPEG data URLs are accepted; anything else (wrong MIME, no base64 marker, raw base64 without the data: prefix, non-base64 characters) is rejected so a corrupt renderer response surfaces immediately. ${index} is 0-based; message reports 1-based slide number.","triggerScenarios":"The desktop renderer returned a data URL with a different MIME (image/webp, image/gif), a plain base64 string without the data: prefix, a URL-encoded image, or garbage/undefined at that array slot.","commonSituations":"Renderer produced image/webp but only png/jpeg are allowed; a slide returned undefined and decode ran against 'undefined' stringified; legacy renderer emitted raw base64; truncation dropped the data: prefix.","solutions":["Switch the render output to PNG or JPEG (pageImageFormat 'png' | 'jpeg') so the data URLs match the accepted MIME types.","Prefer the file-path handoff (readSlideFiles with outputDir) instead of base64 data URLs for large images — it avoids this path entirely.","If the source is genuinely another format, transcode to PNG/JPEG before assembling the data URL."],"exampleFix":"// before: renderer returns webp data URLs (rejected)\ninput.pageImageFormat = 'webp';\nconst images = decodeSlideDataUrls(renderOutput.urls);\n\n// after: use png (or jpeg), or switch to the file-path path\ninput.pageImageFormat = 'png';   // accepted by decodeSlideDataUrls\n// or, better for large images:\nconst images = await readSlideFiles(renderOutput.paths);","handlingStrategy":"validation","validationCode":"const RE = /^data:image\\/(png|jpeg);base64,[A-Za-z0-9+/=]+$/;\nfunction assertAllDataUrls(urls: unknown[]) {\n  urls.forEach((u, i) => {\n    if (!RE.test(u ?? '')) throw new Error(`slide ${i + 1} is not a base64 PNG/JPEG data URL`);\n  });\n}","typeGuard":"function isPngOrJpegDataUrl(u: string): boolean {\n  return /^data:image\\/(png|jpeg);base64,[A-Za-z0-9+/=]+$/.test(u);\n}","tryCatchPattern":"try { decodeSlideDataUrls(urls); }\ncatch (e) {\n  if (e instanceof Error && /not a base64 PNG\\/JPEG data URL/.test(e.message)) {\n    // request PNG/JPEG from the renderer, or switch to readSlideFiles\n  } else throw e;\n}","preventionTips":["Set pageImageFormat to 'png' or 'jpeg' so the renderer emits accepted MIME types.","For large images, use the outputDir file-path handoff (readSlideFiles) instead of data URLs.","Validate each entry against the strict regex before decoding."],"tags":["deck-export","desktop","renderer","validation","base64"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}