NousResearch/hermes-agent · warning · Error

Invalid PDF data URL payload

Error message

Invalid PDF data URL payload

What it means

Third guard in dataUrlToBlob: after metadata validation it runs decodeURIComponent then atob on the payload. A throw here means the payload is not decodable base64 — stray whitespace/newlines from line-wrapped output, URL-unsafe characters, percent-decoding that left invalid base64, or a truncated transfer. The error cleanly separates 'bad encoding' from 'wrong content' (the next guard checks the %PDF- header).

Source

Thrown at apps/desktop/src/app/chat/right-rail/preview-file.tsx:245

  }

  const metadata = dataUrl
    .slice(5, comma)
    .split(';')
    .map(part => part.trim().toLowerCase())

  const payload = dataUrl.slice(comma + 1)

  if (metadata[0] !== 'application/pdf' || !metadata.slice(1).includes('base64')) {
    throw new Error('Invalid PDF data URL type')
  }

  let binary: string

  try {
    binary = atob(decodeURIComponent(payload))
  } catch {
    throw new Error('Invalid PDF data URL payload')
  }

  if (!binary.startsWith('%PDF-')) {
    throw new Error('Invalid PDF file header')
  }

  const bytes = new Uint8Array(binary.length)

  for (let index = 0; index < binary.length; index += 1) {
    bytes[index] = binary.charCodeAt(index)
  }

  return new Blob([bytes], { type: 'application/pdf' })
}

async function readTextPreview(filePath: string) {
  try {
    return await readDesktopFileText(filePath)

View on GitHub (pinned to c896c09c42)

Solutions

  1. Strip whitespace and normalize base64url (-_ to +/, add '=') before decoding.
  2. Re-transfer the PDF — if the payload is truncated, only the source can supply the complete string.
  3. Fix the producer to emit canonical base64 with padding.
  4. For huge PDFs prefer a temp file / IPC path instead of data URLs, which hit URL-length and encoding limits.

Example fix

// before
try {
  binary = atob(decodeURIComponent(payload))
} catch {
  throw new Error('Invalid PDF data URL payload')
}

// after
try {
  const normalized = payload.replace(/\s+/g, '').replace(/-/g, '+').replace(/_/g, '/')
  const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4)
  binary = atob(decodeURIComponent(padded))
} catch {
  throw new Error('Invalid PDF data URL payload')
}
Defensive patterns

Strategy: validation

Validate before calling

const normalizedBase64 = (payload: string) => {
  const clean = payload.replace(/\s+/g, '').replace(/-/g, '+').replace(/_/g, '/')
  return clean + '='.repeat((4 - (clean.length % 4)) % 4)
}
// pre-validate before calling dataUrlToBlob
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(normalizedBase64(payload))) throw new Error('payload is not base64')

Type guard

const isCanonicalBase64 = (s: string): boolean =>
  /^[A-Za-z0-9+/]+={0,2}$/.test(s.replace(/\s+/g, ''))

Try / catch

try { blob = dataUrlToBlob(url) } catch (e) { if (e.message === 'Invalid PDF data URL payload') { /* retry once with normalized base64url->base64 */ } }

Prevention

When it happens

Trigger: Base64 with embedded newlines (MIME-style line wrapping), missing padding, '+/' converted to '-_' (base64url), or payload truncated mid-stream so atob cannot parse it.

Common situations: PDFs relayed through systems that wrap or re-encode base64 (email gateways, JSON pretty-print, log copy-paste); producers using base64url; clipboard truncation of very large data URLs.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/afecc204585498e9. Report an issue: GitHub.