d2lang/d2 · error

invalid sfnt table directory

Error message

invalid sfnt table directory

What it means

The sfnt table directory declares a number of table records (bytes 4–6, 16 bytes each); the parser verifies this count fits within the remaining bytes before iterating. A count larger than the available space means the header is corrupt, so parsing is aborted to avoid out-of-bounds reads.

Source

Thrown at lib/textmeasure/fontface.go:208

	if len(src) < 12 {
		return nil, fmt.Errorf("invalid sfnt: too short")
	}

	base := 0
	if string(src[:4]) == "ttcf" {
		if len(src) < 16 || binary.BigEndian.Uint32(src[8:12]) == 0 {
			return nil, fmt.Errorf("invalid font collection")
		}
		base = int(binary.BigEndian.Uint32(src[12:16]))
		if base < 0 || base+12 > len(src) {
			return nil, fmt.Errorf("invalid font collection offset")
		}
	}

	n := int(binary.BigEndian.Uint16(src[base+4 : base+6]))
	records := base + 12
	if n > (len(src)-records)/16 {
		return nil, fmt.Errorf("invalid sfnt table directory")
	}
	for i := 0; i < n; i++ {
		record := src[records+16*i : records+16*(i+1)]
		if string(record[:4]) != tag {
			continue
		}
		offset := int(binary.BigEndian.Uint32(record[8:12]))
		length := int(binary.BigEndian.Uint32(record[12:16]))
		if offset < 0 || length < 0 || offset > len(src)-length {
			return nil, fmt.Errorf("invalid %s table bounds", tag)
		}
		return src[offset : offset+length], nil
	}
	return nil, nil
}

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Replace the font with an intact copy and verify checksum.
  2. Rebuild the font with fonttools (open and re-save repairs directory consistency).
  3. If the file is a subset intentionally trimmed, re-subset properly so the table directory matches the remaining data.
Defensive patterns

Strategy: validation

Validate before calling

func tableDirFits(data []byte) bool {
    if len(data) < 12 { return false }
    base := 0
    if string(data[:4]) == "ttcf" {
        if len(data) < 16 { return false }
        base = int(binary.BigEndian.Uint32(data[12:16]))
        if base < 0 || base+12 > len(data) { return false }
    }
    n := int(binary.BigEndian.Uint16(data[base+4 : base+6]))
    return n <= (len(data)-base-12)/16
}

Try / catch

if err := loadAndParseFont(path); err != nil {
    log.Printf("font %s has invalid table directory: %v", path, err)
    return defaultFace
}

Prevention

When it happens

Trigger: parseFont on a font whose numTables field exceeds the number of 16-byte records that actually fit in the data — i.e. the file is truncated relative to its declared table count.

Common situations: Interrupted downloads; fonts partially extracted from installers or documents; fuzzed/malformed font files.

Related errors


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