siyuan-note/siyuan · error

font file is invalid

Error message

font file is invalid

What it means

detectCustomFontExtension sniffs the first 4 bytes of the font to detect its type. This error is thrown when reading the 4-byte magic header fails via io.ReadFull — the file is shorter than 4 bytes, unreadable, or an I/O error occurred, so the format cannot even be classified.

Solutions

  1. Verify the file is at least 4 bytes and re-upload it completely
  2. Check the upload pipeline for truncation (Content-Length, temp-file flush/close)
  3. Compare local file size with the source font size to confirm the transfer completed
  4. Re-export or re-download the font from a trusted source

Example fix

// before
font, _, err := util.InstallCustomFont(tempPath)
// after
if info, _ := os.Stat(tempPath); info != nil && info.Size() < 4 {
    return fmt.Errorf("upload truncated (%d bytes)", info.Size())
}
font, _, err := util.InstallCustomFont(tempPath)
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(tempPath); err == nil && info.Size() < 4 {
    return fmt.Errorf("file too small to be a font: %d bytes", info.Size())
}

Try / catch

font, _, err := util.InstallCustomFont(tempPath)
if err != nil && err.Error() == "font file is invalid" {
    // re-upload / verify transfer completeness
}

Prevention

When it happens

Trigger: Calling InstallCustomFont with a temp file of 1-3 bytes (passes the size>0 check but is too small to sniff); a read error on the underlying file; loadCustomFontsLocked encountering a truncated font on disk.

Common situations: Truncated uploads (connection cut mid-transfer); an empty-ish placeholder file a few bytes long; disk/IO errors on the fonts directory; a text file containing a single short line mistakenly uploaded as a font.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at kernel/util/custom_font.go:303

	entries, err := os.ReadDir(CustomFontDir())
	if err != nil {
		return
	}
	for _, entry := range entries {
		if entry.IsDir() || !strings.HasPrefix(entry.Name(), ".font-") {
			continue
		}
		tempPath := filepath.Clean(filepath.Join(CustomFontDir(), entry.Name()))
		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

View on GitHub (pinned to 9f775e8a12)