d2lang/d2 · error

received empty arrow head marker for: %#v

Error message

received empty arrow head marker for: %#v

What it means

When drawing a connection's SVG, if SrcArrow is not NoArrowhead, d2svg builds a start arrowhead marker; arrowheadMarker returns "" when the connection's arrowhead kind has no rendered definition. This is treated as an internal invariant violation and panics. It indicates a d2target.Arrowhead value the SVG renderer cannot translate into a marker.

Source

Thrown at d2renderers/d2svg/d2svg.go:1101

func drawConnection(writer io.Writer, diagramHash string, connection d2target.Connection, markers map[string]struct{}, idToShape map[string]d2target.Shape, sketch bool, inlineTheme *d2themes.Theme, markdown *markdownRenderer) (labelMask string, _ error) {
	opacityStyle := ""
	if connection.Opacity != 1.0 {
		opacityStyle = fmt.Sprintf(" style='opacity:%f'", connection.Opacity)
	}

	classes := []string{base64.URLEncoding.EncodeToString([]byte(svg.EscapeText(connection.ID)))}
	classes = append(classes, connection.Classes...)
	classStr := fmt.Sprintf(` class="%s"`, strings.Join(classes, " "))

	fmt.Fprintf(writer, `<g%s%s>`, classStr, opacityStyle)
	var markerStart string
	if connection.SrcArrow != d2target.NoArrowhead {
		id := arrowheadMarkerID(diagramHash, false, connection)
		if _, in := markers[id]; !in {
			marker := arrowheadMarker(false, id, connection, inlineTheme)
			if marker == "" {
				panic(fmt.Sprintf("received empty arrow head marker for: %#v", connection))
			}
			fmt.Fprint(writer, marker)
			markers[id] = struct{}{}
		}
		markerStart = fmt.Sprintf(`marker-start="url(#%s)" `, id)
	}

	var markerEnd string
	if connection.DstArrow != d2target.NoArrowhead {
		id := arrowheadMarkerID(diagramHash, true, connection)
		if _, in := markers[id]; !in {
			marker := arrowheadMarker(true, id, connection, inlineTheme)
			if marker == "" {
				panic(fmt.Sprintf("received empty arrow head marker for: %#v", connection))
			}
			fmt.Fprint(writer, marker)
			markers[id] = struct{}{}
		}

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Upgrade d2svg/d2target (and D2 generally) so renderer and diagram version match
  2. Inspect connection.SrcArrow for an unexpected value before rendering and normalize it to a supported kind
  3. Re-generate the diagram JSON/AST with the same D2 version used for rendering rather than hand-editing arrowhead values
  4. Patch arrowheadMarker to handle the new arrowhead kind if you added a custom one to d2target

Example fix

// before
conn.SrcArrow = d2target.Arrowhead(99) // unknown
// after
conn.SrcArrow = d2target.ArrowArrowhead
Defensive patterns

Strategy: type-guard

Validate before calling

// Before Render, validate every connection's SrcArrow
for _, c := range diagram.Connections {
    if !supportedArrowhead(c.SrcArrow) {
        return fmt.Errorf("unsupported SrcArrow %d on connection %s", c.SrcArrow, c.ID)
    }
}

Type guard

func supportedArrowhead(a d2target.Arrowhead) bool {
    switch a {
    case d2target.NoArrowhead, d2target.ArrowArrowhead, d2target.TriangleArrowhead,
        d2target.DiamondArrowhead, d2target.CircleArrowhead:
        return true
    }
    return false
}

Try / catch

// Go: recover the panic around Render
defer func() {
    if r := recover(); r != nil {
        if s, ok := r.(string); ok && strings.Contains(s, "empty arrow head marker") {
            err = fmt.Errorf("render failed: %v", r)
        } else { panic(r) }
    }
}()

Prevention

When it happens

Trigger: Calling Render (directly or via renderLegendConnectionIcon) on a diagram whose connection has a non-zero SrcArrow that arrowheadMarker(false, ...) cannot render — i.e. an unsupported/unknown arrowhead type, often from a newer or hand-built d2target AST.

Common situations: Version mismatch where a diagram serialized with a newer D2 arrowhead enum is rendered by an older d2svg; programmatic diagram construction setting SrcArrow to an unregistered kind; legend icons built with unusual arrowhead combinations.

Related errors


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/7fa51d996d395e4f. Report an issue: GitHub.