{"record":{"id":"450043f1564baaad","repo":"crewAIInc/crewAI","slug":"error-reading-pdf-from-file-path-e-s","errorCode":null,"errorMessage":"Error reading PDF from {file_path}: {e!s}","messagePattern":"Error reading PDF from (.+?): (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/rag/loaders/pdf_loader.py","lineNumber":129,"sourceCode":"                if not os.path.isfile(file_path):\n                    raise FileNotFoundError(f\"PDF file not found: {file_path}\")\n                doc = pymupdf.open(file_path)\n\n            # Closed in a finally so a failure mid-extraction still releases the\n            # document handle.\n            try:\n                metadata[\"num_pages\"] = len(doc)\n\n                for page_num, page in enumerate(doc, 1):\n                    page_text = page.get_text()\n                    if page_text.strip():\n                        text_content.append(f\"Page {page_num}:\\n{page_text}\")\n            finally:\n                doc.close()\n        except FileNotFoundError:\n            raise\n        except Exception as e:\n            raise ValueError(f\"Error reading PDF from {file_path}: {e!s}\") from e\n\n        if not text_content:\n            content = f\"[PDF file with no extractable text: {source_name}]\"\n        else:\n            content = \"\\n\\n\".join(text_content)\n\n        return LoaderResult(\n            content=content,\n            source=file_path,\n            metadata=metadata,\n            doc_id=self.generate_doc_id(source_ref=file_path, content=content),\n        )\n","sourceCodeStart":111,"sourceCodeEnd":142,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/rag/loaders/pdf_loader.py#L111-L142","documentation":"Thrown by PDFFileLoader.load when pymupdf (fitz) raises while opening or extracting text from a PDF. The loader wraps any non-FileNotFoundError exception (e.g. pymupdf's RuntimeError for corrupted or encrypted files) into a ValueError with the file path and underlying message. It is a load-time failure of the RAG ingestion step, not a config error.","triggerScenarios":"Calling loader.load(SourceContent(path='broken.pdf')) or RagService ingestion on a file that fitz.open() cannot parse: truncated downloads, password-protected/encrypted PDFs, zero-byte files, or files with a .pdf extension that are not actually PDFs. Also triggered if the file disappears between the existence check and open (TOCTOU) or by unsupported/odd PDF structures.","commonSituations":"Ingesting user-uploaded or scraped documents where some downloads are truncated; encrypted PDFs from financial/government sites; a pipeline pointed at a directory with mixed file types; older pymupdf versions failing on newer PDF features.","solutions":["Verify the file is a real, complete PDF before loading (check magic bytes '%PDF-' and that it ends with '%%EOF' or re-download it).","Test opening it directly: python -c \"import fitz; d=fitz.open('file.pdf'); print(d.page_count)\" to surface the raw pymupdf error.","If the PDF is encrypted, decrypt it first with the password (fitz Document.authenticate) or with a tool like qpdf before ingestion.","Upgrade pymupdf to the latest version (uv add 'pymupdf>=1.24') since parse failures are often fixed upstream.","If the file genuinely does not exist, let the FileNotFoundError propagate (the loader re-raises it unchanged) and fix the path."],"exampleFix":"// before\nresult = pdf_loader.load(SourceContent(path=\"report.pdf\"))\n\n# after\nimport fitz\n\ndef is_readable_pdf(path: str) -> bool:\n    try:\n        doc = fitz.open(path)\n        doc.close()\n        return True\n    except Exception:\n        return False\n\nif is_readable_pdf(\"report.pdf\"):\n    result = pdf_loader.load(SourceContent(path=\"report.pdf\"))\nelse:\n    log.warning(\"skipping unreadable PDF: %s\", \"report.pdf\")","handlingStrategy":"try-catch","validationCode":"import fitz\n\ndef pdf_is_loadable(path: str) -> bool:\n    try:\n        doc = fitz.open(path)\n        try:\n            if doc.is_encrypted and not doc.authenticate(\"\"):\n                return False\n            _ = doc[0].get_text()\n            return True\n        finally:\n            doc.close()\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    result = pdf_loader.load(src)\nexcept FileNotFoundError:\n    skip(src)  # missing file, distinct signal\nexcept ValueError as e:\n    log.warning(\"pdf parse failed src=%s cause=%r\", src.source, e.__cause__)\n    quarantine(src)","preventionTips":["Validate downloads with a magic-byte check (%PDF- header) before ingestion.","Skip or password-decrypt encrypted PDFs upstream of the RAG pipeline.","Batch-ingest with per-file try/except so one bad PDF does not kill the run.","Keep pymupdf current; most parse regressions are fixed upstream."],"tags":["pdf","rag","file-io","pymupdf"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}