{"record":{"id":"7fe4e3b54e35fe4b","repo":"deepset-ai/haystack","slug":"the-mermaid-server-response-does-not-look-like-a-v","errorCode":null,"errorMessage":"The Mermaid server response does not look like a valid {expected_label}. This can happen if 'server_url' points to a server that is not a Mermaid renderer. To avoid writing untrusted content to disk, no file will be saved.","messagePattern":"The Mermaid server response does not look like a valid (.+?)\\. This can happen if 'server_url' points to a server that is not a Mermaid renderer\\. To avoid writing untrusted content to disk, no file will be saved\\.","errorType":"exception","errorClass":"PipelineDrawingError","httpStatus":null,"severity":"error","filePath":"haystack/core/pipeline/draw.py","lineNumber":240,"sourceCode":"        content_type_prefixes = (\"image/webp\",)\n        body_ok = content[0:4] == _RIFF_SIGNATURE and content[8:12] == _WEBP_SIGNATURE\n    else:  # png (default)\n        expected_label = \"PNG image\"\n        content_type_prefixes = (\"image/png\",)\n        body_ok = content.startswith(_PNG_SIGNATURE)\n\n    # The Content-Type header is server-controlled, so a mismatch is only a warning: the\n    # authoritative check is the body signature below.\n    content_type = resp.headers.get(\"content-type\", \"\").split(\";\")[0].strip().lower()\n    if content_type and not content_type.startswith(content_type_prefixes):\n        logger.warning(\n            \"The Mermaid server returned an unexpected Content-Type '{content_type}' (expected {expected}).\",\n            content_type=content_type,\n            expected=expected_label,\n        )\n\n    if not body_ok:\n        raise PipelineDrawingError(\n            f\"The Mermaid server response does not look like a valid {expected_label}. \"\n            f\"This can happen if 'server_url' points to a server that is not a Mermaid renderer. \"\n            f\"To avoid writing untrusted content to disk, no file will be saved.\"\n        )\n\n\ndef _to_mermaid_image(\n    graph: networkx.MultiDiGraph,\n    server_url: str = \"https://mermaid.ink\",\n    params: dict | None = None,\n    timeout: int = 30,\n    super_component_mapping: dict[str, str] | None = None,\n) -> bytes:\n    \"\"\"\n    Renders a pipeline using a Mermaid server.\n\n    :param graph:\n        The graph to render as a Mermaid pipeline.","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/core/pipeline/draw.py#L222-L258","documentation":"Haystack checks the magic bytes of the Mermaid server's response body against the requested format (PNG/SVG/PDF signatures) so untrusted content is never written to disk. If the body does not match the expected format — typically because server_url points to something that is not a Mermaid renderer — this PipelineDrawingError is raised.","triggerScenarios":"server_url points to an HTML page, auth portal, or non-renderer service that returns HTML/JSON/text instead of image bytes; or the server returns an image in a different format than requested (e.g., PNG returned when PDF asked).","commonSituations":"Corporate proxies returning login pages, wrong port hitting a different service, typo'd server_url, self-hosted renderer misconfigured to always serve a default page, or API gateways that intercept and rewrite responses.","solutions":["Verify server_url points to an actual Mermaid renderer and test with curl -i","Check whether a proxy/firewall is intercepting the response (inspect response headers/logs)","If self-hosting, confirm the renderer serves the requested content type","Retry with the default https://mermaid.ink to isolate the issue"],"exampleFix":"// before\npipeline.draw(path=\"graph.pdf\", server_url=\"http://localhost:8080\")  # serves HTML\n// after\npipeline.draw(path=\"graph.pdf\", server_url=\"https://mermaid.ink\")","handlingStrategy":"try-catch","validationCode":"r = requests.get(server_url, timeout=10)\nct = r.headers.get(\"content-type\", \"\")\nif \"html\" in ct.lower():\n    raise RuntimeError(f\"{server_url} is not a Mermaid renderer (returned HTML)\")","typeGuard":"def looks_like_mermaid_server(url: str) -> bool:\n    try:\n        r = requests.get(url, timeout=5)\n        return \"html\" not in r.headers.get(\"content-type\", \"\").lower()\n    except requests.RequestException:\n        return False","tryCatchPattern":"try:\n    pipeline.draw(path=\"graph.png\", server_url=URL)\nexcept PipelineDrawingError as e:\n    if \"does not look like a valid\" in str(e):\n        logger.error(\"server_url misconfigured: %s\", URL)\nelse:\n    ...","preventionTips":["Never point server_url at proxies/portals; test with curl -i first","Check for corporate proxy interference","Pin a known-good renderer for automated diagram generation"],"tags":["network","security","mermaid","content-validation"],"backgroundTag":"invalid-content-type","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}