{"record":{"id":"afecc204585498e9","repo":"NousResearch/hermes-agent","slug":"invalid-pdf-data-url-payload","errorCode":null,"errorMessage":"Invalid PDF data URL payload","messagePattern":"Invalid PDF data URL payload","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/desktop/src/app/chat/right-rail/preview-file.tsx","lineNumber":245,"sourceCode":"  }\n\n  const metadata = dataUrl\n    .slice(5, comma)\n    .split(';')\n    .map(part => part.trim().toLowerCase())\n\n  const payload = dataUrl.slice(comma + 1)\n\n  if (metadata[0] !== 'application/pdf' || !metadata.slice(1).includes('base64')) {\n    throw new Error('Invalid PDF data URL type')\n  }\n\n  let binary: string\n\n  try {\n    binary = atob(decodeURIComponent(payload))\n  } catch {\n    throw new Error('Invalid PDF data URL payload')\n  }\n\n  if (!binary.startsWith('%PDF-')) {\n    throw new Error('Invalid PDF file header')\n  }\n\n  const bytes = new Uint8Array(binary.length)\n\n  for (let index = 0; index < binary.length; index += 1) {\n    bytes[index] = binary.charCodeAt(index)\n  }\n\n  return new Blob([bytes], { type: 'application/pdf' })\n}\n\nasync function readTextPreview(filePath: string) {\n  try {\n    return await readDesktopFileText(filePath)","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/src/app/chat/right-rail/preview-file.tsx#L227-L263","documentation":"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).","triggerScenarios":"Base64 with embedded newlines (MIME-style line wrapping), missing padding, '+/' converted to '-_' (base64url), or payload truncated mid-stream so atob cannot parse it.","commonSituations":"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.","solutions":["Strip whitespace and normalize base64url (-_ to +/, add '=') before decoding.","Re-transfer the PDF — if the payload is truncated, only the source can supply the complete string.","Fix the producer to emit canonical base64 with padding.","For huge PDFs prefer a temp file / IPC path instead of data URLs, which hit URL-length and encoding limits."],"exampleFix":"// before\ntry {\n  binary = atob(decodeURIComponent(payload))\n} catch {\n  throw new Error('Invalid PDF data URL payload')\n}\n\n// after\ntry {\n  const normalized = payload.replace(/\\s+/g, '').replace(/-/g, '+').replace(/_/g, '/')\n  const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4)\n  binary = atob(decodeURIComponent(padded))\n} catch {\n  throw new Error('Invalid PDF data URL payload')\n}","handlingStrategy":"validation","validationCode":"const normalizedBase64 = (payload: string) => {\n  const clean = payload.replace(/\\s+/g, '').replace(/-/g, '+').replace(/_/g, '/')\n  return clean + '='.repeat((4 - (clean.length % 4)) % 4)\n}\n// pre-validate before calling dataUrlToBlob\nif (!/^[A-Za-z0-9+/]*={0,2}$/.test(normalizedBase64(payload))) throw new Error('payload is not base64')","typeGuard":"const isCanonicalBase64 = (s: string): boolean =>\n  /^[A-Za-z0-9+/]+={0,2}$/.test(s.replace(/\\s+/g, ''))","tryCatchPattern":"try { blob = dataUrlToBlob(url) } catch (e) { if (e.message === 'Invalid PDF data URL payload') { /* retry once with normalized base64url->base64 */ } }","preventionTips":["Normalize base64 (strip whitespace, map base64url alphabet, restore padding) before decode","Avoid data URLs for large documents; use temp files or IPC","Verify payload length % 4 == 0 before atob"],"tags":["desktop","preview","pdf","base64","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}