{"record":{"id":"cf448e81d94372df","repo":"Stirling-Tools/Stirling-PDF","slug":"response-is-not-a-valid-pdf-header-head","errorCode":null,"errorMessage":"Response is not a valid PDF. Header: \"${head}\"","messagePattern":"Response is not a valid PDF\\. Header: \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/hooks/tools/ocr/useOCROperation.ts","lineNumber":147,"sourceCode":"  }\n\n  // Not a PDF: surface error details if present\n  if (!head.startsWith(\"%PDF\")) {\n    const textBuf = await blob.slice(0, 1024).arrayBuffer();\n    const text = new TextDecoder().decode(new Uint8Array(textBuf));\n    if (/error|exception|html/i.test(text)) {\n      if (text.includes(\"OCR tools\") && text.includes(\"not installed\")) {\n        throw new Error(\n          \"OCR tools (OCRmyPDF or Tesseract) are not installed on the server. Use the standard or fat Docker image instead of ultra-lite, or install OCR tools manually.\",\n        );\n      }\n      const title =\n        text.match(/<title[^>]*>([^<]+)<\\/title>/i)?.[1] ||\n        text.match(/<h1[^>]*>([^<]+)<\\/h1>/i)?.[1] ||\n        \"Unknown error\";\n      throw new Error(`OCR service error: ${title}`);\n    }\n    throw new Error(`Response is not a valid PDF. Header: \"${head}\"`);\n  }\n\n  const originalName = originalFiles[0].name;\n  return [new File([blob], originalName, { type: \"application/pdf\" })];\n};\n\n// Static configuration object (without t function dependencies)\nexport const ocrOperationConfig = defineSingleFileTool({\n  validateParams: validateOCRParameters,\n  buildFormData: buildOCRFormData,\n  toApiParams: ocrToApiParams,\n  fromApiParams: ocrFromApiParams,\n  operationType: \"ocr\",\n  endpoint: ENDPOINT,\n  defaultParameters,\n});\n\nexport const useOCROperation = () => {","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/hooks/tools/ocr/useOCROperation.ts#L129-L165","documentation":"Thrown by the OCR response handler when the blob's first 8 bytes are neither a PDF magic ('%PDF') nor a ZIP magic ('PK'), and the first 1KB does not look like an error/exception/HTML body. It is the 'unrecognized response' fallback — the server returned something, but it is neither a valid OCR output nor a recognizable error.","triggerScenarios":"Server returned a 200 with an empty body or whitespace; response is a binary fragment of an unknown format; a CDN/cache layer returned a truncated response; content-encoding/transfer corruption truncated the magic bytes; the OCR endpoint returned an octet-stream that is not actually a PDF.","commonSituations":"Empty 200 from a misconfigured endpoint; truncated response on a flaky connection; the backend changed its output format without the frontend handler being updated.","solutions":["Log the full Content-Type, Content-Length, and first bytes for the failing response to classify it.","Check backend logs — a 200 with empty body usually means the controller returned before writing output (early return / swallowed exception).","Verify network/proxy is not truncating the body (compare Content-Length to actual bytes).","Treat HTTP non-2xx explicitly before parsing the body so error responses are not misclassified as 'not a PDF'."],"exampleFix":"// before\nthrow new Error(`Response is not a valid PDF. Header: \"${head}\"`);\n\n// after — include size + content-type for diagnosis\nthrow new Error(`Response is not a valid PDF. Header: \"${head}\" (size=${blob.size}, type=${blob.type})`);","handlingStrategy":"validation","validationCode":"// Reject non-PDF responses before parsing\nif (!head.startsWith(\"%PDF\") && !head.startsWith(\"PK\")) {\n  const ct = response.headers?.[\"content-type\"];\n  if (!ct?.includes(\"pdf\") && !ct?.includes(\"zip\")) {\n    // classify as protocol error with status + size, do not attempt PDF parsing\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await runOcr();\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Response is not a valid PDF.\")) {\n    // log size/type and check backend for empty-body bug\n    console.error(\"Unrecognized OCR response\", { size: blob.size, type: blob.type });\n    toast.error(\"OCR returned an unexpected response.\");\n  } else throw e;\n}","preventionTips":["Verify the endpoint actually returned a PDF (check Content-Type/Length).","Handle HTTP non-2xx explicitly so error responses are not misclassified as 'not a PDF'.","Log the raw response metadata when this fires to classify the unknown format."],"tags":["ocr","response-parsing","validation","network"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}