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:
-
Load the Graphviz model – The
_load_pygraphviz_agraphfunction builds apygraphviz.AGraphobject 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. -
Render to SVG and parse – The
graph.draw(prog=layout_prog, format="svg")method produces an SVG byte-string thatparse_nodes_edges_clustersingraphviz2drawio/models/SvgParser.py(lines 23–81) transforms into internal nodes, edges, and clusters objects. This extraction handles gradients, titles, and parallel edge merging. -
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 theMxGraphclass 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:
- Print the
edge.key_for_labelfor each parsed edge ingraphviz2drawio/models/SvgParser.py(lines 64–73) - Compare with the
graph_edgesdictionary created ingraphviz2drawio/graphviz2drawio.py(lines 19–24) - If keys differ, the SVG uses multi-line labels—adjust parsing or supply explicit
xlabel/labelattributes in the DOT file
Wrong Geometry or Positions
Typical symptom: Nodes appear in incorrect locations or edges appear stretched.
Debug actions:
- Dump
Rectobjects usingprint(node.rect)to verify parsed coordinates - Verify the coordinate translation in
CoordsTranslate.from_svg_transformmatches the SVGtransformattribute - Ensure the graph’s
layout_progproduces non-scaled SVG using-Gsizeor-Gdpioptions
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-colororstop-opacitystyles inSvgParser._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/exceptto 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.DEBUGto watch internal dictionary construction ingraphviz2drawio.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 ofmygraph.gv.svg. - Inspect SVG structure: Open
mygraph.gv.svgin 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))afterparse_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
graphviz2drawio/graphviz2drawio.py: PublicconvertAPI and orchestration. Critical functions include_load_pygraphviz_agraphand attribute enrichment (lines 39–50).graphviz2drawio/models/Errors.py: Custom exception hierarchy. ContainsMissingTitleError(lines 22–28) andUnableToParseGraphError(lines 56–62).graphviz2drawio/models/SvgParser.py: SVG parsing and entity extraction. Key logic inparse_nodes_edges_clusters(lines 23–81) and_extract_gradients(lines 96–135).graphviz2drawio/mx/MxGraph.py: Draw.io XML generation. Coordinate handling inadd_mx_geo_with_points(lines 30–48).graphviz2drawio/mx/Edge.py: Edge styling and geometry calculations inget_edge_style(lines 64–73).
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 genericExceptionto 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 -Tsvgindependently 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →