{"record":{"id":"122efd1a105635e7","repo":"deepset-ai/haystack","slug":"the-mermaid-server-returned-an-empty-response-no","errorCode":null,"errorMessage":"The Mermaid server returned an empty response; no image will be saved.","messagePattern":"The Mermaid server returned an empty response; no image will be saved\\.","errorType":"exception","errorClass":"PipelineDrawingError","httpStatus":null,"severity":"error","filePath":"haystack/core/pipeline/draw.py","lineNumber":200,"sourceCode":"    \"\"\"\n    Validate that the Mermaid server response actually contains the expected image/SVG/PDF data.\n\n    `Pipeline.draw()` writes the raw response body to disk, so a misconfigured or malicious\n    `server_url` could otherwise cause arbitrary content (e.g. an HTML error page or a crafted\n    payload) to be written verbatim to the output path. As defense-in-depth we check both the\n    `Content-Type` header (which the server controls and could spoof) and the response body's\n    magic-byte signature (which is harder to forge while still producing a usable payload).\n\n    :param resp:\n        The HTTP response returned by the Mermaid server.\n    :param params:\n        Validated Mermaid parameters; used to determine the expected output format.\n    :raises PipelineDrawingError:\n        If the response is empty or does not match the expected format.\n    \"\"\"\n    content = resp.content\n    if not content:\n        raise PipelineDrawingError(\"The Mermaid server returned an empty response; no image will be saved.\")\n\n    output_format = params.get(\"format\", \"img\")\n    img_type = params.get(\"type\", \"png\")\n\n    # (human-readable label, expected Content-Type prefix, body signature check)\n    content_type_prefixes: tuple[str, ...]\n    if output_format == \"svg\":\n        expected_label = \"SVG\"\n        content_type_prefixes = (\"image/svg+xml\", \"text/xml\", \"application/xml\")\n        stripped = content.lstrip()[:512].lower()\n        body_ok = stripped.startswith(_SVG_PREFIXES) or _SVG_PREFIXES[1] in stripped\n    elif output_format == \"pdf\":\n        expected_label = \"PDF\"\n        content_type_prefixes = (\"application/pdf\",)\n        body_ok = content.startswith(_PDF_SIGNATURE)\n    elif img_type == \"jpeg\":\n        expected_label = \"JPEG image\"\n        content_type_prefixes = (\"image/jpeg\",)","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/core/pipeline/draw.py#L182-L218","documentation":"Before saving a pipeline diagram, Haystack validates the HTTP response from the Mermaid rendering server. If the response body is empty, no image exists to save, so a PipelineDrawingError is raised to avoid writing an empty/corrupt file to disk.","triggerScenarios":"Calling Pipeline.draw() against a Mermaid server (mermaid.ink or self-hosted, via server_url) that returns an HTTP 200 (or any non-exception) response with an empty body.","commonSituations":"A misconfigured server_url pointing to a proxy or load balancer that answers with empty bodies, a rate-limited server returning empty responses, a self-hosted mermaid renderer that failed internally, or a captive-portal/HTML gateway swallowing the request.","solutions":["Check server_url is a working Mermaid renderer (e.g., https://mermaid.ink) and reachable with curl","Inspect the logged warning/stacktrace from the underlying request for the actual failure","If self-hosting, check the renderer's logs and scale/restart it","Retry after transient network issues"],"exampleFix":"// before\npipeline.draw(path=\"graph.png\", server_url=\"http://internal-proxy\")\n// after\npipeline.draw(path=\"graph.png\", server_url=\"https://mermaid.ink\")","handlingStrategy":"try-catch","validationCode":"import requests\nr = requests.head(server_url, timeout=10)\nif r.status_code >= 400:\n    raise RuntimeError(f\"Mermaid server {server_url} unreachable\")","typeGuard":"def server_reachable(url: str) -> bool:\n    try:\n        return requests.get(url, timeout=5).status_code < 400\n    except requests.RequestException:\n        return False","tryCatchPattern":"from haystack.core.errors import PipelineDrawingError\ntry:\n    pipeline.draw(path=\"graph.png\")\nexcept PipelineDrawingError as e:\n    logger.error(\"Mermaid render failed: %s\", e)  # fall back / skip diagram","preventionTips":["Verify server_url with curl before automated runs","Handle rate limits and outages with retries","Prefer self-hosted renderer in CI where internet is restricted"],"tags":["network","http","mermaid","pipeline-drawing"],"backgroundTag":"empty-server-response","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}