d2lang/d2 · error

text not pre-measured and no ruler provided

Error message

text not pre-measured and no ruler provided

What it means

GetTextDimensionsWithMono reached its terminal fallback: the text was not found in the pre-measured mtexts list, the ruler is nil (or couldn't measure), and the trimmed text is non-empty. It returns this sentinel error meaning measurement was impossible — callers like the label/edge sizing paths surface it or treat nil as 'not found'.

Source

Thrown at d2graph/d2graph.go:1453

func getMarkdownDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler, t *d2target.MText, fontFamily *d2fonts.FontFamily, monoFontFamily *d2fonts.FontFamily) (*d2target.TextDimensions, error) {
	if dims := findMeasured(mtexts, t); dims != nil {
		return dims, nil
	}

	if ruler != nil {
		width, height, err := textmeasure.MeasureMarkdown(t.Text, ruler, fontFamily, monoFontFamily, t.FontSize)
		if err != nil {
			return nil, err
		}
		return d2target.NewTextDimensions(width, height), nil
	}

	if strings.TrimSpace(t.Text) == "" {
		return d2target.NewTextDimensions(1, 1), nil
	}

	return nil, fmt.Errorf("text not pre-measured and no ruler provided")
}

func GetTextDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler, t *d2target.MText, fontFamily *d2fonts.FontFamily) *d2target.TextDimensions {
	return GetTextDimensionsWithMono(mtexts, ruler, t, fontFamily, nil)
}

func GetTextDimensionsWithMono(mtexts []*d2target.MText, ruler *textmeasure.Ruler, t *d2target.MText, fontFamily *d2fonts.FontFamily, monoFontFamily *d2fonts.FontFamily) *d2target.TextDimensions {
	if dims := findMeasured(mtexts, t); dims != nil {
		return dims
	}

	if ruler != nil {
		var w int
		var h int
		if t.Language != "" {
			originalLineHeight := ruler.LineHeightFactor
			ruler.LineHeightFactor = textmeasure.CODE_LINE_HEIGHT
			if monoFontFamily != nil {

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Create and pass a textmeasure.Ruler (textmeasure.NewRuler() with fonts initialized) so text is measured on demand
  2. Ensure mtexts contains an exact MText match — same Text, FontSize, fontFamily, bold/italic — for every measured string
  3. Regenerate mtexts via graph.SetText after any text or style change
  4. If the text can legitimately be empty, it's already handled: empty/whitespace text returns 1x1 dimensions without error

Example fix

// before
dims := d2graph.GetTextDimensions(mtexts, nil, mt, fontFamily)
// after
ruler, _ := textmeasure.NewRuler()
dims := d2graph.GetTextDimensions(mtexts, ruler, mt, fontFamily)
Defensive patterns

Strategy: validation

Validate before calling

// guard before calling GetTextDimensions without a ruler
func measure(mtexts []*d2target.MText, t *d2target.MText, ff *d2fonts.FontFamily) (*d2target.TextDimensions, error) {
    if strings.TrimSpace(t.Text) == "" {
        return d2target.NewTextDimensions(1, 1), nil
    }
    if ruler == nil {
        for _, mt := range mtexts {
            if mt.Text == t.Text && mt.FontSize == t.FontSize {
                // only safe if an exact pre-measured entry exists
                return d2target.NewTextDimensions(mt.Width, mt.Height), nil
            }
        }
        return nil, fmt.Errorf("text %q not pre-measured; ruler required", t.Text)
    }
    return d2graph.GetTextDimensions(mtexts, ruler, t, ff), nil
}

Type guard

func isPreMeasured(mtexts []*d2target.MText, t *d2target.MText) bool {
    for _, mt := range mtexts {
        if mt.Text == t.Text && mt.FontSize == t.FontSize &&
            mt.FontFamily == t.FontFamily && mt.Bold == t.Bold && mt.Italic == t.Italic {
            return true
        }
    }
    return false
}

Try / catch

dims := d2graph.GetTextDimensions(mtexts, ruler, mt, fontFamily)
if dims == nil {
    return fmt.Errorf("text %q not pre-measured and no ruler provided", mt.Text)
}

Prevention

When it happens

Trigger: Calling GetTextDimensions/GetTextDimensionsWithMono with a nil ruler for a non-empty text that has no matching entry in mtexts (same text, fontSize, font family, and bold/italic flags).

Common situations: Embedding d2graph without textmeasure (e.g. no fonts bundled); passing mtexts generated before text/font changes; mismatched MText attributes causing lookup misses; custom render pipelines skipping graph.SetText.

Related errors


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