siyuan-note/siyuan · error
only TTF and OTF font files are supported
Error message
only TTF and OTF font files are supported
What it means
The font sniffer only recognizes TrueType (magic \x00\x01\x00\x00 or 'true') and OpenType/CFF ('OTTO') headers. This error is thrown when the first 4 bytes match neither, meaning the file is not a supported TTF/OTF font — commonly a WOFF/WOFF2, a .ttc collection, or an entirely different file type renamed to .ttf.
Solutions
- Convert the font to TTF/OTF first (e.g. fonttools: ttx/woff2 decompress, or an online woff2->ttf converter)
- Extract a single .ttf from a .ttc collection before installing
- Verify the download is actually a font (check the magic bytes: 00 01 00 00, 'true', or 'OTTO') and not an HTML/error page
- Use a plain (non-web) TTF/OTF build of the font from the foundry
Example fix
// before
curl -o font.ttf https://example.com/font.woff2
util.InstallCustomFont("font.ttf") // rejected
// after
woff2_decompress font.woff2 # produces font.ttf
util.InstallCustomFont("font.ttf") // OK: TTF/OTF only Defensive patterns
Strategy: validation
Validate before calling
func sniffFont(b []byte) string {
switch string(b[:4]) {
case "\x00\x01\x00\x00", "true": return ".ttf"
case "OTTO": return ".otf"
default: return ""
}
} Try / catch
font, _, err := util.InstallCustomFont(tempPath)
if err != nil && strings.Contains(err.Error(), "only TTF and OTF") {
// ask user to convert (woff2/ttc -> ttf/otf) and retry
} Prevention
- Accept only .ttf/.otf uploads client-side and sniff magic bytes before staging
- Convert WOFF/WOFF2 to TTF before installing
- Never rely on file extension; check the header
When it happens
Trigger: Calling InstallCustomFont with a WOFF/WOFF2 webfont, a TrueType Collection (.ttc), a renamed ZIP/archive, or any non-font file; also triggered by loadCustomFontsLocked on files in the custom fonts dir that fail sniffing (silently skipped there).
Common situations: Browsers/services that only offered WOFF2 downloads; users renaming .woff2 or .ttc to .ttf hoping it works; downloading an HTML error page saved as a font; picking variable-font TTC bundles.
Related errors
- unsupported server type
- 106
- 199
- active tab [ ] is not a direct child of [ ]
- AI editor action must not be empty
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/67eaa96adcc9a68b.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/custom_font.go:312
if _, active := customFontTemps[tempPath]; !active {
_ = os.Remove(tempPath)
}
}
}
func detectCustomFontExtension(reader io.Reader) (string, error) {
header := make([]byte, 4)
if _, err := io.ReadFull(reader, header); err != nil {
return "", errors.New("font file is invalid")
}
switch string(header) {
case "\x00\x01\x00\x00", "true":
return ".ttf", nil
case "OTTO":
return ".otf", nil
default:
return "", errors.New("only TTF and OTF font files are supported")
}
}
func customFontHash(reader io.Reader) (string, error) {
hash := sha256.New()
if _, err := io.Copy(hash, reader); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
func parseCustomFontFile(fontFile *os.File) (ret *Font, err error) {
defer func() {
if recovered := recover(); recovered != nil {
ret = nil
err = fmt.Errorf("parse font failed: %v", recovered)
}
}()View on GitHub (pinned to 9f775e8a12)