{"record":{"id":"c42659f16ad254df","repo":"crewAIInc/crewAI","slug":"reading-pdf-urls-requires-pymupdf-install-with-u","errorCode":null,"errorMessage":"Reading PDF URLs requires pymupdf. Install with: uv add pymupdf","messagePattern":"Reading PDF URLs requires pymupdf\\. Install with: uv add pymupdf","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/tools/url_read_tool/url_read_tool.py","lineNumber":248,"sourceCode":"    def _decode(self, body: bytes, content_type: str) -> str:\n        \"\"\"Decode *body* using the configured, declared, or default encoding.\n\n        Falls back to a replacing UTF-8 decode rather than failing: partially\n        readable text is more useful to an agent than an error.\n        \"\"\"\n        encoding = self.encoding or _charset_from_content_type(content_type) or \"utf-8\"\n        try:\n            return body.decode(encoding)\n        except (LookupError, UnicodeDecodeError):\n            return body.decode(\"utf-8\", errors=\"replace\")\n\n    @staticmethod\n    def _extract_pdf(body: bytes) -> str:\n        \"\"\"Extract text from PDF bytes, page by page.\"\"\"\n        try:\n            import pymupdf  # type: ignore[import-untyped]\n        except ImportError as e:\n            raise ImportError(\n                \"Reading PDF URLs requires pymupdf. Install with: uv add pymupdf\"\n            ) from e\n\n        # Opened from memory: the bytes are already in hand, and a temp file\n        # would need cleaning up on every error path.\n        document = pymupdf.open(stream=body, filetype=\"pdf\")\n        try:\n            pages = [\n                f\"Page {number}:\\n{text}\"\n                for number, page in enumerate(document, 1)\n                if (text := page.get_text().strip())\n            ]\n        finally:\n            document.close()\n\n        if not pages:\n            return \"[PDF with no extractable text]\"\n        return \"\\n\\n\".join(pages)","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/tools/url_read_tool/url_read_tool.py#L230-L266","documentation":"UrlReadTool can fetch and parse PDF URLs, but PDF extraction lazily imports the optional 'pymupdf' package inside _extract_pdf. If pymupdf is not installed in the environment, reading a PDF URL raises ImportError with the install command.","triggerScenarios":"Calling UrlReadTool on a URL whose Content-Type is application/pdf (or a .pdf link) in an environment where pymupdf is not installed.","commonSituations":"Installing crewai-tools without the PDF extra; upgrading environments and dropping optional deps; only testing the tool against HTML pages so the PDF code path is never exercised until production.","solutions":["Install the dependency: uv add pymupdf (or pip install pymupdf).","Add pymupdf to your project dependencies if any workflow may read PDF URLs.","Catch ImportError around the read call and fall back to skipping/caching PDF URLs."],"exampleFix":"# before\ntool = UrlReadTool()\ntool.run('https://example.com/report.pdf')  # ImportError: requires pymupdf\n\n# after\n# shell: uv add pymupdf\ntool.run('https://example.com/report.pdf')","handlingStrategy":"validation","validationCode":"import importlib.util\n\nif not importlib.util.find_spec('pymupdf'):\n    # decide policy: skip PDFs or fail\n    raise SystemExit('UrlReadTool needs pymupdf for PDF URLs: uv add pymupdf')","typeGuard":null,"tryCatchPattern":"try:\n    text = tool.run(url)\nexcept ImportError as e:\n    if 'pymupdf' in str(e):\n        text = None  # or mark URL unsupported and continue\n    else:\n        raise","preventionTips":["Install pymupdf whenever your agent may fetch PDF links.","Pre-filter URLs by extension/content type against installed parsers.","Degrade gracefully: catch ImportError per-URL and report which parser is missing."],"tags":["dependencies","pdf","pymupdf","url-read"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}