siyuan-note/siyuan · error

parse font failed: %w

Error message

parse font failed: %w

What it means

Returned by parseCustomFontFile when sfnt.Parse returns a non-nil error (without panicking). Unlike the panic branch (errorIndex 1089), this is the normal error path: the parser recognized the input as invalid and returned an error, which is wrapped with %w so the underlying cause is preserved for errors.Is/As.

Source

Thrown at kernel/util/custom_font.go:329

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
	}
	decoded, err := hex.DecodeString(id)
	return err == nil && len(decoded) == sha256.Size
}

func newCustomFont(id, fontPath string, font *Font) *CustomFont {
	weight := font.Weight

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Inspect the wrapped error (errors.Unwrap or %w chain) to see the specific sfnt parse failure, then address that cause.
  2. Replace the font from a known-good source.
  3. Validate with fonttools/ttx to confirm the table structure before re-uploading.

Example fix

// before
if _, _, err := util.InstallCustomFont(tempPath); err != nil {
    log.Println(err)
}

// after
if _, _, err := util.InstallCustomFont(tempPath); err != nil {
    var parseErr *sfnt.ParseError // if the underlying type is exposed
    if errors.As(err, &parseErr) {
        log.Printf("sfnt parse failure: %v", parseErr)
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

if _, _, err := util.InstallCustomFont(tempPath); err != nil {
    var inner = err
    for errors.Unwrap(inner) != nil { inner = errors.Unwrap(inner) }
    // inner now holds the underlying sfnt.Parse cause; branch on it
}

Prevention

When it happens

Trigger: InstallCustomFont or loadCustomFontsLocked passes a file whose header looks like TTF/OTF but whose internal structure (offset table, table directory, required tables) is invalid, so sfnt.Parse rejects it.

Common situations: A partially downloaded or truncated font (correct header, missing tables); a file that was concatenated or patched; an OTF whose CFF data is malformed.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/17db7a160c72e170. Report an issue: GitHub.