NousResearch/hermes-agent · warning · Error

Invalid PDF file header

Error message

Invalid PDF file header

What it means

Final content guard in dataUrlToBlob: after successful base64 decode it requires the decoded bytes to start with the literal '%PDF-' magic. Anything else means the payload decoded fine but is not a PDF (HTML error page, PNG, JSON, plain text). This stops the preview from handing a non-PDF blob to the PDF renderer, which would otherwise fail with a cryptic renderer error.

Source

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

    .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)
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error)

    if (!message.includes("No handler registered for 'hermes:readFileText'")) {

View on GitHub (pinned to c896c09c42)

Solutions

  1. Re-fetch the source document and verify with `curl -I` / `file` that it really is a PDF.
  2. Fix upstream classification so the attachment kind reflects actual bytes, not the filename extension.
  3. If the payload is HTML/text, route it to the text/HTML preview instead.
  4. For producers: set the data URL MIME from the real content type of the streamed bytes.

Example fix

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

// after — give the user the actual detected type in the error
const header = binary.slice(0, 8)
if (!binary.startsWith('%PDF-')) {
  throw new Error(`Invalid PDF file header (got: ${JSON.stringify(header)})`)
}
Defensive patterns

Strategy: validation

Validate before calling

const decodedHead = atob(payload.slice(0, 8))
if (!decodedHead.startsWith('%PDF-')) {
  // route to text/html/image preview based on sniffed magic bytes instead of failing
}

Type guard

const hasPdfMagic = (bytes: Uint8Array): boolean =>
  bytes[0] === 0x25 /* % */ && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46

Try / catch

try { blob = dataUrlToBlob(url) } catch (e) { if (e.message === 'Invalid PDF file header') { offerDownload(url) /* let the user open it externally */ } }

Prevention

When it happens

Trigger: An application/pdf data URL whose bytes are actually HTML (common when a download endpoint returned an error page but the caller labeled it as PDF), a screenshot image mislabeled as PDF, or an encrypted/patched PDF variant without the standard header offset 0.

Common situations: Signed/expired download links returning HTML login pages saved as .pdf; tools labeling all document artifacts 'pdf'; zero-byte or header-shifted payloads after bad decoding.

Related errors


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