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

  1. Switch the render output to PNG or JPEG (pageImageFormat 'png' | 'jpeg') so the data URLs match the accepted MIME types.
  2. Prefer the file-path handoff (readSlideFiles with outputDir) instead of base64 data URLs for large images — it avoids this path entirely.
  3. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/13584dd2d2480bfb. Report an issue: GitHub.