d2lang/d2 · error
invalid kern table: no subtables
Error message
invalid kern table: no subtables
What it means
After the kern table header, nSubtables (bytes 2–4) must be non-zero; a version-0 kern table with zero subtables contains no kerning data at all, which the parser treats as malformed rather than empty.
Source
Thrown at lib/textmeasure/fontface.go:165
} else {
x -= int64(unitsPerEm) / 2
}
return fixed.Int26_6(x / int64(unitsPerEm))
}
func parseLegacyKern(src []byte) ([]kernPair, error) {
table, err := sfntTable(src, "kern")
if err != nil || table == nil {
return nil, err
}
if len(table) < 18 {
return nil, fmt.Errorf("invalid kern table: too short")
}
if binary.BigEndian.Uint16(table[0:2]) != 0 {
return nil, fmt.Errorf("unsupported kern table version")
}
if binary.BigEndian.Uint16(table[2:4]) == 0 {
return nil, fmt.Errorf("invalid kern table: no subtables")
}
length := int(binary.BigEndian.Uint16(table[6:8]))
coverage := binary.BigEndian.Uint16(table[8:10])
if coverage != 0x0001 {
return nil, fmt.Errorf("unsupported kern table coverage 0x%04x", coverage)
}
n := int(binary.BigEndian.Uint16(table[10:12]))
if length < 14 || 6*n != length-14 || 4+length > len(table) {
return nil, fmt.Errorf("invalid kern table length")
}
pairs := make([]kernPair, n)
for i := range pairs {
offset := 18 + 6*i
pairs[i] = kernPair{
key: binary.BigEndian.Uint32(table[offset : offset+4]),
value: int16(binary.BigEndian.Uint16(table[offset+4 : offset+6])),View on GitHub (pinned to 0d69dca6f5)
Solutions
- Remove the empty kern table entirely with fonttools so the parser skips it gracefully.
- Regenerate/re-download the font from the original source.
- Verify with fonttools (f['kern'].kernTables) that the table actually contains subtables; if kerning lives in GPOS, accept reduced kerning fidelity or pre-convert the font.
Example fix
# python fonttools: drop the empty kern table
from fontTools.ttLib import TTFont
f = TTFont('broken.ttf')
if 'kern' in f and not f['kern'].kernTables:
del f['kern']
f.save('fixed.ttf') Defensive patterns
Strategy: fallback
Validate before calling
// nSubtables := binary.BigEndian.Uint16(kern[2:4]); usable := nSubtables != 0
Try / catch
pairs, err := parseLegacyKern(fontData)
if err != nil {
log.Printf("kerning unavailable (%v), continuing without", err)
pairs = nil
} Prevention
- Inspect fonts with fonttools before bundling; delete empty kern tables
- Regenerate fonts from source rather than editing binaries in place
- Keep GPOS-based fonts as primary assets; legacy kern is best-effort
- Fail fast in CI by parsing every bundled font at test time
When it happens
Trigger: parseFont loads a font whose kern table declares version 0 but nSubtables == 0, typically from a broken subsetting or editing operation.
Common situations: Fonts processed by buggy subsetting tools; fonts where GPOS kerning was added and the legacy kern table emptied but not removed.
Related errors
- invalid kern table: too short
- unsupported kern table version
- unsupported kern table coverage 0x%04x
- invalid kern table length
- checksum error in table: %v
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/1d633d533a44a44e.
Report an issue: GitHub.