{"record":{"id":"b0482d7df5ef1f70","repo":"nexu-io/open-design","slug":"slide-index-1-has-no-file-path","errorCode":null,"errorMessage":"slide ${index + 1} has no file path","messagePattern":"slide (.+?) has no file path","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/deck-export.ts","lineNumber":109,"sourceCode":"}\n\n/**\n * Decodes the `data:image/(png|jpeg);base64,...` URLs the desktop renderer\n * returns into raw image buffers tagged with their format. Rejects anything that\n * is not a base64 PNG/JPEG data URL so a malformed renderer response surfaces as\n * an export failure rather than a corrupt file.\n */\n/**\n * Reads the image files the desktop renderer wrote (the `outputDir` handoff)\n * into raw buffers tagged with their format (by extension). The companion to\n * {@link decodeSlideDataUrls} for the file-path path, which avoids shuttling\n * 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","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/deck-export.ts#L91-L127","documentation":"Thrown by readSlideFiles() (the file-path variant of slide ingestion) when one of the paths the desktop renderer handed back is not a non-empty string — i.e. undefined, null, a number, or '' at that array index. ${index} is 0-based, so the message reports the 1-based slide number. It surfaces a malformed renderer response as an export failure rather than a silent skip or a corrupt file.","triggerScenarios":"The desktop renderer's outputDir handoff returned an array where one entry is missing/empty — e.g. a slide failed to render and produced no file, or the IPC payload was assembled with a hole.","commonSituations":"Renderer crashed on one slide and returned '' for it; partial render where some slides were skipped; an array length mismatch between requested and rendered slides; race where the renderer reported paths before all files were written.","solutions":["Re-run the render so every slide produces a file path; retry export once the desktop renderer reports all slides complete.","Filter or pad the paths array only if missing slides are genuinely intentional (rare) — normally the export should fail loudly, which is what this error does.","Check the desktop renderer logs for the failing slide (the index in the message points at it) and fix the render-side cause."],"exampleFix":"// before: passing the renderer array straight through\nconst images = await readSlideFiles(renderOutput.paths);\n\n// after: assert every slide produced a path before reading\nif (renderOutput.paths.some(p => typeof p !== 'string' || p.length === 0)) {\n  throw new Error('render returned an incomplete slide set');\n}\nconst images = await readSlideFiles(renderOutput.paths);","handlingStrategy":"validation","validationCode":"function assertAllPathsPresent(paths: unknown[]) {\n  paths.forEach((p, i) => {\n    if (typeof p !== 'string' || p.length === 0) {\n      throw new Error(`slide ${i + 1} has no file path`);\n    }\n  });\n}","typeGuard":"function allSlidePathsValid(paths: unknown[]): paths is string[] {\n  return paths.every(p => typeof p === 'string' && p.length > 0);\n}","tryCatchPattern":"try { await readSlideFiles(paths); }\ncatch (e) {\n  if (e instanceof Error && /has no file path/.test(e.message)) {\n    // re-render the missing slide, then retry\n  } else throw e;\n}","preventionTips":["Have the desktop renderer report per-slide completion so missing paths are visible upstream.","Validate the paths array length matches the slide count before reading.","Prefer the file-path handoff over base64 data URLs for reliability."],"tags":["deck-export","desktop","renderer","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}