siyuan-note/siyuan · error

parse font failed

Error message

parse font failed: %v

What it means

parseCustomFontFile wraps sfnt.Parse and also recovers from panics inside the font parser. This variant (with %v) is the panic-recovery path: the sfnt library panicked while parsing the font, so the panic is converted into this error with the recovered value embedded. It guards the kernel against malformed fonts crashing the process.

Solutions

  1. Validate the font with an independent tool (fonttools/ttx or fontforge) to confirm it is corrupt
  2. Re-obtain the font file from a trusted source and reinstall
  3. Delete the corrupt file from appearance/fonts/custom so loading stops failing
  4. If it reproduces on a legitimate font, report the sfnt parser panic upstream
Defensive patterns

Strategy: try-catch

Try / catch

font, _, err := util.InstallCustomFont(tempPath)
if err != nil && strings.HasPrefix(err.Error(), "parse font failed") {
    // font binary is malformed; quarantine the file and do not retry blindly
}

Prevention

When it happens

Trigger: Calling InstallCustomFont or loadCustomFontsLocked on a font whose header passed the 4-byte magic sniff but whose tables are corrupt enough to make github.com/ConradIrwin/font/sfnt panic during Parse.

Common situations: Maliciously or accidentally corrupted font files; partially downloaded fonts that kept a valid magic header; fuzzed/hand-edited font binaries in the custom fonts directory.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/3c540964a55dc789. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/custom_font.go:328

		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)
		}
	}()

	parsed, err := sfnt.Parse(fontFile)
	if err != nil {
		return nil, fmt.Errorf("parse font failed: %w", err)
	}
	ret, err = parseFontInfo(parsed)
	if err != nil {
		return nil, fmt.Errorf("parse font metadata failed: %w", err)
	}
	return ret, nil
}

func validCustomFontID(id string) bool {
	if len(id) != sha256.Size*2 || strings.ToLower(id) != id {
		return false
	}

View on GitHub (pinned to 9f775e8a12)