apache/beam · error

render DOT failed

Error message

render DOT failed

What it means

dot.Render writes a Beam pipeline graph as Graphviz DOT text using Go text/template execution against node/edge templates. If any template Execute call fails (rare — template parse is checked at init, so failures here are writer errors or internal template bugs), the error is wrapped as "render DOT failed".

Solutions

  1. Unwrap with errors.Unwrap / %v on the cause to see the underlying writer error, then fix that (disk space, permissions, connection).
  2. Verify the io.Writer passed to dot.Render is open and writable for the duration of the call.
  3. If piping DOT output, ensure the downstream process (e.g. `dot -Tsvg`) is alive and reading; check its stderr.
  4. Render to an in-memory bytes.Buffer first, then write the buffer to the destination with explicit error handling.

Example fix

// before
f, _ := os.Create("graph.dot")
dot.Render(p, f) // fails if disk full, cause hidden

// after
var buf bytes.Buffer
if err := dot.Render(p, &buf); err != nil {
    return fmt.Errorf("render: %w", err)
}
os.WriteFile("graph.dot", buf.Bytes(), 0644)
Defensive patterns

Strategy: fallback

Validate before calling

func writable(w io.Writer) error {
    if f, ok := w.(*os.File); ok {
        if _, err := f.Stat(); err != nil {
            return err
        }
    }
    return nil
}

Type guard

if f, ok := w.(*os.File); ok {
    if _, err := f.Stat(); err != nil {
        // writer is unusable; don't attempt Render
    }
}

Try / catch

var buf bytes.Buffer
if err := dot.Render(p, &buf); err != nil {
    return fmt.Errorf("render DOT failed: %w", err)
}
if _, err := w.Write(buf.Bytes()); err != nil {
    return fmt.Errorf("write DOT failed: %w", err)
}

Prevention

When it happens

Trigger: Calling dot.Render(p, w) where the underlying io.Writer returns an error during template execution (e.g. closed file, disk full, broken pipe), or an internal template data mismatch causes Execute to fail.

Common situations: Users render pipeline DOT to a file on a full filesystem, to a network mount that dropped, or piping to a process that exited early (os/exec pipe closed); also when generating DOT from a harness where the writer is an HTTP body that errored.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/util/dot/dot.go:129

	for _, node := range nodes {
		if seen[node] {
			continue
		}
		seen[node] = true
		err := nodeTmpl.Execute(w, struct{ Name, Label string }{node.String(), uniqNodes[node].String()})
		if err != nil {
			return err
		}
	}

	for _, edge := range edges {
		e := fmt.Sprintf("%d: %s", edge.ID(), edge.Op)
		label := fmt.Sprint(edge.Op)
		if name := path.Base(edge.Name()); name != label {
			label = fmt.Sprintf("%s\n%s", edge.Op, name)
		}
		if err := edgeDefnTmpl.Execute(w, struct{ Name, Label string }{e, label}); err != nil {
			return errors.Wrap(err, "render DOT failed")
		}
		for _, ib := range edge.Input {
			err := edgeTmpl.Execute(w, struct{ From, To string }{ib.From.String(), e})
			if err != nil {
				return errors.Wrap(err, "render DOT failed")
			}
		}
		for _, ob := range edge.Output {
			uniqNodes[ob.To].From = ob
			err := edgeTmpl.Execute(w, struct{ From, To string }{e, ob.To.String()})
			if err != nil {
				return errors.Wrap(err, "render DOT failed")
			}
		}
	}
	w.Write([]byte(footer))
	return nil
}

View on GitHub (pinned to 12126d8942)