{"record":{"id":"825872f5f09b05fd","repo":"siyuan-note/siyuan","slug":"font-file-is-empty","errorCode":null,"errorMessage":"font file is empty","messagePattern":"font file is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/custom_font.go","lineNumber":103,"sourceCode":"\nfunc LoadCustomFonts() []*CustomFont {\n\tcustomFontsLock.Lock()\n\tdefer customFontsLock.Unlock()\n\n\tloadCustomFontsLocked()\n\treturn cloneCustomFonts(customFonts)\n}\n\nfunc InstallCustomFont(tempPath string) (*CustomFont, bool, error) {\n\tcustomFontsLock.Lock()\n\tdefer customFontsLock.Unlock()\n\n\tinfo, err := os.Stat(tempPath)\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\tif !info.Mode().IsRegular() || info.Size() < 1 {\n\t\treturn nil, false, errors.New(\"font file is empty\")\n\t}\n\tif MaxCustomFontSize < info.Size() {\n\t\treturn nil, false, errors.New(\"font file is too large\")\n\t}\n\n\tfontFile, err := os.Open(tempPath)\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\n\textension, err := detectCustomFontExtension(fontFile)\n\tif err != nil {\n\t\tfontFile.Close()\n\t\treturn nil, false, err\n\t}\n\tif _, err = fontFile.Seek(0, io.SeekStart); err != nil {\n\t\tfontFile.Close()\n\t\treturn nil, false, err","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/custom_font.go#L85-L121","documentation":"Returned by InstallCustomFont when os.Stat reports the temp file is not a regular file, or has a size below 1 byte. The check guards against empty or non-file uploads before any font parsing. The temp file comes from CreateCustomFontTemp, which writes into the custom-fonts directory.","triggerScenarios":"Calling util.InstallCustomFont(tempPath) where tempPath points to an empty file (0 bytes), a directory, a device/socket, a symlink to nothing, or any non-regular file.","commonSituations":"The upload was interrupted so the temp file ended up empty; the frontend created the temp file but never streamed body bytes into it; a previous DiscardCustomFontTemp left a zero-byte file behind; the temp path points at a directory by mistake.","solutions":["Verify the upload actually wrote bytes to the temp file before calling InstallCustomFont (check the upload handler writes the full request body).","Confirm tempPath was created by CreateCustomFontTemp and points at a file under CustomFontDir(), not a directory or symlink.","Re-run the font upload from the start so a fresh temp file is allocated and filled."],"exampleFix":"// before\nfont, _, err := util.InstallCustomFont(tempPath)\n\n// after\nif info, serr := os.Stat(tempPath); serr != nil || !info.Mode().IsRegular() || info.Size() < 1 {\n    return errors.New(\"upload produced an empty file\")\n}\nfont, _, err := util.InstallCustomFont(tempPath)","handlingStrategy":"validation","validationCode":"func validFontUpload(tempPath string) error {\n    info, err := os.Stat(tempPath)\n    if err != nil { return err }\n    if !info.Mode().IsRegular() || info.Size() < 1 {\n        return errors.New(\"upload produced an empty file\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify the upload handler writes the full request body and closes the temp file before handing the path off.","Stat the temp file at the API boundary so empty uploads are rejected with a clear message."],"tags":["fonts","validation","upload","filesystem"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}