apache/beam · error · RuntimeError

Graphviz dot executable not available for serving

Error message

Graphviz dot executable not available for serving

What it means

If the Graphviz 'dot' executable is missing and --render_port was requested, the renderer cannot serve the pipeline graph over HTTP and raises RuntimeError chained from the original 'dot -V' failure. Rendering to a live page requires dot even though only serving was requested.

Solutions

  1. Install Graphviz (apt-get install graphviz / brew install graphviz) and confirm 'dot -V' works
  2. Add graphviz's install location to PATH
  3. Drop --render_port and write a .dot file instead if graphviz cannot be installed

Example fix

# before
--render_port 8099   # host without graphviz -> RuntimeError
# after
apt-get install -y graphviz && python -m apache_beam.runners.render ... --render_port 8099
Defensive patterns

Strategy: fallback

Validate before calling

import shutil
if serve_port >= 0 and shutil.which('dot') is None:
    raise SystemExit('Graphviz required for --render_port serving; install graphviz or drop --render_port')

Type guard

def can_serve_render(render_port: int) -> bool:
    import shutil
    return render_port < 0 or shutil.which('dot') is not None

Try / catch

try:
    result = pipeline.run()
except RuntimeError as e:
    if 'not available for serving' in str(e):
        rerun_without_port(render_output='pipeline.dot')  # file fallback
    else:
        raise

Prevention

When it happens

Trigger: Launching run_portable_pipeline with --render_port >= 0 (e.g. --render_port 8099) on a host where the graphviz 'dot' binary is absent, so subprocess 'dot -V' check fails.

Common situations: Docker/CI images lacking graphviz while attempting the interactive render web UI; PATH not containing dot despite graphviz being installed elsewhere.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/266dd3cb8b54096a. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/runners/render.py:432

      subprocess.run(['dot', '-V'], capture_output=True, check=True)
    except FileNotFoundError as exn:
      # If dot is not available, we can at least output the raw .dot files.
      dot_files = [
          output for output in render_options.render_output
          if output.endswith('.dot')
      ]
      for output in dot_files:
        with open(output, 'w') as fout:
          fout.write(renderer.to_dot())
          _LOGGER.info("Wrote pipeline as %s", output)

      non_dot_files = set(render_options.render_output) - set(dot_files)
      if non_dot_files:
        raise RuntimeError(
            "Graphviz dot executable not available "
            f"for rendering non-dot output files {non_dot_files}") from exn
      elif render_options.render_port >= 0:
        raise RuntimeError(
            "Graphviz dot executable not available for serving") from exn

      return RenderPipelineResult(None)

    renderer.page()

    if render_options.render_port >= 0:
      # TODO: If this gets more complex, we could consider taking on a
      # framework like Flask as a dependency.
      class RequestHandler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
          parts = urllib.parse.urlparse(self.path)
          args = urllib.parse.parse_qs(parts.query)
          renderer.update(**args)

          if parts.path == '/':
            response = renderer.page()
          elif parts.path == '/render':

View on GitHub (pinned to 12126d8942)