d2lang/d2 · error

ruler does not have entire font family %s loaded, is a style

Error message

ruler does not have entire font family %s loaded, is a style missing?

What it means

SetDimensions guards its measurement work: if a ruler is supplied, it must have every style variant (regular, bold, italic, etc.) of the given font family loaded, checked via ruler.HasFontFamilyLoaded. A partially loaded ruler means some labels would measure incorrectly, so sizing fails fast with this error.

Source

Thrown at d2graph/d2graph.go:1524

			w, h = ruler.Measure(fontFamily.Font(t.FontSize, style), t.Text)
		}
		return d2target.NewTextDimensions(w, h)
	}

	return nil
}

func appendTextDedup(texts []*d2target.MText, t *d2target.MText) []*d2target.MText {
	if GetTextDimensions(texts, nil, t, nil) == nil {
		return append(texts, t)
	}
	return texts
}

func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily, monoFontFamily *d2fonts.FontFamily) error {
	if ruler != nil && fontFamily != nil {
		if ok := ruler.HasFontFamilyLoaded(fontFamily); !ok {
			return fmt.Errorf("ruler does not have entire font family %s loaded, is a style missing?", *fontFamily)
		}
	}
	if ruler != nil && monoFontFamily != nil {
		if ok := ruler.HasFontFamilyLoaded(monoFontFamily); !ok {
			return fmt.Errorf("ruler does not have entire mono font family %s loaded, is a style missing?", *monoFontFamily)
		}
	}

	if g.Theme != nil && g.Theme.SpecialRules.Mono {
		tmp := d2fonts.SourceCodePro
		fontFamily = &tmp
	}

	for _, obj := range g.Objects {
		obj.Box = &geo.Box{}

		// user-specified label/icon positions
		if obj.HasLabel() && obj.Attributes.LabelPosition != nil {

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Initialize the ruler with the full d2fonts default set (ruler.InitFonts(d2fonts.D2Fonts)) so all variants are loaded
  2. If using custom fonts, register every style variant (regular, bold, italic, bold-italic) under the same family name
  3. Pass nil fontFamily only when all texts are guaranteed pre-measured in mtexts
  4. Check ruler.HasFontFamilyLoaded yourself before calling SetDimensions and fail with a clearer message

Example fix

// before
ruler, _ := textmeasure.NewRuler() // no fonts loaded
err := graph.SetDimensions(mtexts, ruler, &fontFamily, nil) // fails
// after
ruler, _ := textmeasure.NewRuler()
ruler.InitFonts(d2fonts.D2Fonts)
err := graph.SetDimensions(mtexts, ruler, &fontFamily, nil)
Defensive patterns

Strategy: validation

Validate before calling

ruler, _ := textmeasure.NewRuler()
ruler.InitFonts(d2fonts.D2Fonts)
if !ruler.HasFontFamilyLoaded(&fontFamily) {
    return fmt.Errorf("font family %s incomplete; register all style variants before SetDimensions", fontFamily.Name)
}
err := graph.SetDimensions(mtexts, ruler, &fontFamily, monoFontFamily)

Type guard

func fontFamilyReady(ruler *textmeasure.Ruler, ff *d2fonts.FontFamily) bool {
    return ruler != nil && ff != nil && ruler.HasFontFamilyLoaded(ff)
}

Try / catch

if err := graph.SetDimensions(mtexts, ruler, fontFamily, mono); err != nil {
    if strings.Contains(err.Error(), "does not have entire font family") {
        ruler.InitFonts(d2fonts.D2Fonts) // or register missing variants, then retry
        err = graph.SetDimensions(mtexts, ruler, fontFamily, mono)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Graph.SetDimensions with a non-nil ruler and non-nil fontFamily where ruler.HasFontFamilyLoaded(fontFamily) returns false — e.g. a custom Ruler that only loaded a subset of the family's font style files.

Common situations: Using a custom-compiled textmeasure.Ruler with self-hosted fonts missing bold/italic variants; fonts embedded but family name mismatched (style applied via near/connections makes D2 request variants); upgrading d2 and forgetting to register newly required font files.

Related errors


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