nexu-io/open-design · error · Error
slide ${index + 1} is not a base64 PNG/JPEG data URL
Error message
slide ${index + 1} is not a base64 PNG/JPEG data URL What it means
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.
Source
Thrown at apps/daemon/src/deck-export.ts:121
* base64 image bytes through the JSON IPC channel for large images.
*/
export async function readSlideFiles(paths: string[]): Promise<SlideImage[]> {
return Promise.all(
paths.map(async (filePath, index) => {
if (typeof filePath !== 'string' || filePath.length === 0) {
throw new Error(`slide ${index + 1} has no file path`);
}
const buffer = await readFile(filePath);
return { buffer, jpeg: /\.jpe?g$/i.test(filePath) };
}),
);
}
export function decodeSlideDataUrls(urls: string[]): SlideImage[] {
return urls.map((url, index) => {
const match = /^data:image\/(png|jpeg);base64,([A-Za-z0-9+/=]+)$/.exec(url ?? '');
if (!match) {
throw new Error(`slide ${index + 1} is not a base64 PNG/JPEG data URL`);
}
return { buffer: Buffer.from(match[2] ?? '', 'base64'), jpeg: match[1] === 'jpeg' };
});
}
// Standard PowerPoint 16:9 slide is 13.333" x 7.5". We keep 13.333" as the slide
// width and derive the height from the deck's actual aspect ratio, so a 4:3,
// square, or portrait deck gets a correctly-proportioned slide instead of being
// letterboxed into a 16:9 frame.
const PPTX_SLIDE_WIDTH_IN = 13.333;
/**
* Assembles per-slide images into a screenshot-based .pptx — one full-bleed
* image per slide. The slide aspect ratio follows the deck's authored size
* (`opts.aspect` = width/height); falls back to 16:9. The slides are
* pixel-perfect images (not editable text), the "exactly what you see" export
* mode. Returns the .pptx bytes.
*/View on GitHub (pinned to 5be4028344)
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.
Example fix
// before: renderer returns webp data URLs (rejected) input.pageImageFormat = 'webp'; const images = decodeSlideDataUrls(renderOutput.urls); // after: use png (or jpeg), or switch to the file-path path input.pageImageFormat = 'png'; // accepted by decodeSlideDataUrls // or, better for large images: const images = await readSlideFiles(renderOutput.paths);
Defensive patterns
Strategy: validation
Validate before calling
const RE = /^data:image\/(png|jpeg);base64,[A-Za-z0-9+/=]+$/;
function assertAllDataUrls(urls: unknown[]) {
urls.forEach((u, i) => {
if (!RE.test(u ?? '')) throw new Error(`slide ${i + 1} is not a base64 PNG/JPEG data URL`);
});
} Type guard
function isPngOrJpegDataUrl(u: string): boolean {
return /^data:image\/(png|jpeg);base64,[A-Za-z0-9+/=]+$/.test(u);
} Try / catch
try { decodeSlideDataUrls(urls); }
catch (e) {
if (e instanceof Error && /not a base64 PNG\/JPEG data URL/.test(e.message)) {
// request PNG/JPEG from the renderer, or switch to readSlideFiles
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- slide ${index + 1} has no file path
- no slides to export
- invalid JSON in ${filePath}: ${message}
- ${filePath} must contain a JSON object
- ARTIFACT_MANIFEST_INVALID
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/13584dd2d2480bfb.
Report an issue: GitHub.