How to Debug Graphviz-to-Draw.io Conversion Issues with graphviz2drawio

Enable logging.DEBUG, catch specific exceptions like MissingTitleError or UnableToParseGraphError, and inspect the intermediate SVG output to isolate whether failures originate from pygraphviz, the SVG parser, or the XML generation stage.

The graphviz2drawio library transforms Graphviz DOT files into Draw.io-compatible XML through a multi-stage pipeline involving pygraphviz, SVG rendering, and custom attribute enrichment. When debugging conversion failures, you must identify which of the three conversion stages—loading the Graphviz model, parsing the SVG, or generating the final XML—is producing the error. This guide provides targeted debugging strategies based on the actual source code implementation in the hbmartin/graphviz2drawio repository.

Understanding the Conversion Pipeline

The conversion process in graphviz2drawio/graphviz2drawio.py executes three distinct operations that serve as failure isolation points:

  1. Load the Graphviz model – The _load_pygraphviz_agraph function builds a pygraphviz.AGraph object from a file path, DOT string, or file-like object. This stage includes detection of DOT strings, handling of comments, and a bug workaround for pygraphviz issue #55.

  2. Render to SVG and parse – The graph.draw(prog=layout_prog, format="svg") method produces an SVG byte-string that parse_nodes_edges_clusters in graphviz2drawio/models/SvgParser.py (lines 23–81) transforms into internal nodes, edges, and clusters objects. This extraction handles gradients, titles, and parallel edge merging.

  3. Enrich attributes and build XML – The code enriches parsed edges and nodes with original Graphviz attribute dictionaries (lines 39–45 for edges, lines 48–50 for nodes in graphviz2drawio.py), then the MxGraph class writes the final Draw.io XML structure.

Because the pipeline spans multiple subsystems, errors typically manifest in specific stages with distinct symptoms.

Common Failure Types and Solutions

Unable to Render SVG

Typical symptom: UnableToParseGraphError raised from graphviz2drawio/models/Errors.py (lines 56–62) with the message that graph.draw() returned None.

Debug actions:

  • Verify pygraphviz installation: python -c "import pygraphviz"
  • Test the layout program manually: dot -Tsvg mygraph.gv > out.svg
  • Confirm the layout program (dot, neato, etc.) is available in your system $PATH

Missing Title in SVG Elements

Typical symptom: MissingTitleError raised from graphviz2drawio/models/Errors.py (lines 22–28), indicating a node or edge lacks a title attribute.

Debug actions:

  • Open the generated SVG and inspect <g class="node"> or <g class="edge"> elements for missing preceding comments (<!-- node_id -->)
  • Ensure the original DOT file supplies labels using [label="..."] attributes or comment lines (// ...)

Incorrect Edge Label Merging

Typical symptom: Duplicate edge labels appear or labels are lost entirely.

Debug actions:

Wrong Geometry or Positions

Typical symptom: Nodes appear in incorrect locations or edges appear stretched.

Debug actions:

  • Dump Rect objects using print(node.rect) to verify parsed coordinates
  • Verify the coordinate translation in CoordsTranslate.from_svg_transform matches the SVG transform attribute
  • Ensure the graph’s layout_prog produces non-scaled SVG using -Gsize or -Gdpi options

Missing or Malformed Gradients

Typical symptom: Colors fallback to black or transparent instead of the defined gradient.

Debug actions:

  • Inspect <linearGradient> or <radialGradient> sections in the SVG
  • Look for missing stop-color or stop-opacity styles in SvgParser._extract_gradients (lines 96–135)
  • Provide explicit style="stop-color:#RRGGBB;stop-opacity:1" in the DOT file or upgrade Graphviz to a version generating more robust gradients

Unexpected Exceptions

Typical symptom: Traceback not covered by the custom error hierarchy.

Debug actions:

  • Wrap conversion in try/except to print the full traceback
  • Enable logging.basicConfig(level=logging.DEBUG) to see internal state including node counts, edge counts, and SVG size

Step-by-Step Debugging Workflow

Use this complete script to isolate failures across the conversion pipeline:

import logging
from pathlib import Path
from graphviz2drawio import convert
from graphviz2drawio.models.Errors import (
    UnableToParseGraphError,
    MissingTitleError,
)

logging.basicConfig(level=logging.DEBUG)   # View internal debug output

graph_path = Path("examples/broken.gv")

try:
    drawio_xml = convert(graph_path, layout_prog="dot")
    Path("out.drawio").write_text(drawio_xml, encoding="utf-8")
    print("✅ conversion succeeded")
except MissingTitleError as e:
    print("❌ missing title:", e)
except UnableToParseGraphError as e:
    print("❌ graphviz could not produce SVG:", e)
except Exception as e:                     # Catch-all for unexpected bugs

    import traceback
    traceback.print_exc()

Key isolation techniques illustrated:

  • Import specific exception classes (MissingTitleError, UnableToParseGraphError) to distinguish between parsing and generation failures
  • Enable logging.DEBUG to watch internal dictionary construction in graphviz2drawio.py (e.g., graph_edges, graph_nodes)
  • Write resulting XML to disk for visual verification in Draw.io

Quick Diagnostic Checks

  • Verify pygraphviz functionality: Run python -c "import pygraphviz; print(pygraphviz.__version__)". Look for: No ImportError confirms installation.
  • Test the layout program: Run dot -Tsvg -O mygraph.gv. Look for: Successful generation of mygraph.gv.svg.
  • Inspect SVG structure: Open mygraph.gv.svg in any viewer. Look for: <g class="node"> elements should have preceding comments (<!-- node_id -->).
  • Count parsed entities: Add print(len(nodes), len(edges), len(clusters)) after parse_nodes_edges_clusters. Look for: Counts should match the number of elements in the original DOT file.

Practical Debug Code Examples

Minimal Conversion with Error Handling

This example demonstrates the standard entry point with proper exception handling:

from pathlib import Path
from graphviz2drawio import convert
from graphviz2drawio.models.Errors import UnableToParseGraphError, MissingTitleError

dot_file = Path("mygraph.gv")          # Can also be a string with DOT source

try:
    drawio_xml = convert(dot_file)     # Default layout program is "dot"

    Path("mygraph.drawio").write_text(drawio_xml)
    print("✅ Draw.io file written")
except MissingTitleError as exc:
    print("❌ A node or edge is missing a title:", exc)
except UnableToParseGraphError as exc:
    print("❌ Graphviz could not generate SVG:", exc)

References: convert implementation (graphviz2drawio.py lines 13–50), custom errors (Errors.py lines 22–28).

Inspecting Intermediate SVG Output

When visual output is wrong, examine the SVG before XML conversion:

from graphviz2drawio import _load_pygraphviz_agraph

g = _load_pygraphviz_agraph("mygraph.gv")
svg_bytes = g.draw(prog="dot", format="svg")
Path("debug.svg").write_bytes(svg_bytes)      # Open in browser to inspect

print("🔍 SVG written – inspect titles/comments for nodes/edges")

Reference: SVG generation logic (graphviz2drawio.py lines 29–33).

Logging Internal Parsing Statistics

Verify that the SVG parser correctly identifies all entities:

import logging
from graphviz2drawio.models.SvgParser import parse_nodes_edges_clusters

logging.basicConfig(level=logging.DEBUG)

# Assuming svg_bytes obtained from previous step

nodes, edges, clusters = parse_nodes_edges_clusters(svg_bytes, is_directed=True)

logging.debug("Parsed %d nodes, %d edges, %d clusters",
              len(nodes), len(edges), len(clusters))

Reference: Parser entry point (SvgParser.py lines 23–28).

Key Source Files for Debugging

Summary

  • Isolate the failure stage using the three-phase pipeline: AGraph loading (pygraphviz), SVG parsing (SvgParser.py), or XML generation (MxGraph.py).
  • Catch specific exceptions (MissingTitleError, UnableToParseGraphError) rather than generic Exception to identify whether the DOT file, SVG output, or coordinate translation is at fault.
  • Enable debug logging to view internal counts of nodes, edges, and clusters during the conversion process.
  • Inspect intermediate SVG manually when geometry or styling appears incorrect in the final Draw.io output.
  • Verify system dependencies by running dot -Tsvg independently to ensure Graphviz layout programs are correctly installed and accessible in $PATH.

Frequently Asked Questions

Why does the conversion fail with "Unable to parse graph" even though my DOT file works in other tools?

This error originates from UnableToParseGraphError in graphviz2drawio/models/Errors.py when graph.draw() returns None. The issue typically indicates that pygraphviz cannot find the Graphviz layout program (e.g., dot) in your system path, or the DOT syntax contains constructs that pygraphviz cannot process. Run dot -Tsvg yourfile.gv manually to verify the layout engine works independently of Python.

How do I fix missing titles for nodes or edges in the generated Draw.io diagram?

The MissingTitleError in graphviz2drawio/models/Errors.py (lines 22–28) triggers when the SVG parser cannot find a comment or label attribute identifying an element. Ensure your DOT file includes explicit labels like [label="NodeName"] for nodes, or that the Graphviz comment format (// node_id) is preserved in the SVG output. Open the intermediate SVG file to verify that <g class="node"> elements have preceding HTML comments containing the element ID.

Why are edge labels duplicated or missing after conversion?

This occurs when edge.key_for_label values in graphviz2drawio/models/SvgParser.py (lines 64–73) do not match the keys in the graph_edges dictionary built in graphviz2drawio/graphviz2drawio.py (lines 19–24). Multi-line labels or special characters in the DOT file can cause key mismatches. Supply explicit xlabel attributes in your DOT file or print the edge keys during debugging to identify the specific label causing the merge conflict.

How can I verify that coordinate positions are being calculated correctly?

Dump the Rect objects from parsed nodes using print(node.rect) and compare them with the SVG's transform attribute processed by CoordsTranslate.from_svg_transform. If positions appear wrong in Draw.io, check that your Graphviz command does not apply scaling factors—avoid -Gsize attributes that resize the output, or ensure DPI settings match between the layout program and the SVG parser expectations in graphviz2drawio/mx/MxGraph.py (lines 30–48).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →