{"record":{"id":"1f2e796c6f41c4ae","repo":"siyuan-note/siyuan","slug":"custom-emoji-file-must-not-be-empty","errorCode":null,"errorMessage":"custom emoji file must not be empty","messagePattern":"custom emoji file must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/system.go","lineNumber":355,"sourceCode":"\t}\n\tdefer response.Body.Close()\n\tif response.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"download custom emoji failed with status %d\", response.StatusCode)\n\t}\n\tif response.ContentLength > maxCustomEmojiSize {\n\t\treturn nil, fmt.Errorf(\"custom emoji file is too large\")\n\t}\n\n\tdata, err := io.ReadAll(io.LimitReader(response.Body, maxCustomEmojiSize+1))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read custom emoji response failed: %w\", err)\n\t}\n\treturn data, nil\n}\n\nfunc normalizeCustomEmojiData(data []byte) (normalized []byte, ext string, err error) {\n\tif len(data) == 0 {\n\t\treturn nil, \"\", fmt.Errorf(\"custom emoji file must not be empty\")\n\t}\n\n\traster := true\n\tswitch http.DetectContentType(data) {\n\tcase \"image/png\":\n\t\text = \".png\"\n\tcase \"image/jpeg\":\n\t\text = \".jpg\"\n\tcase \"image/gif\":\n\t\text = \".gif\"\n\tcase \"image/webp\":\n\t\text = \".webp\"\n\tdefault:\n\t\traster = false\n\t}\n\tif raster {\n\t\tconfig, _, decodeErr := image.DecodeConfig(bytes.NewReader(data))\n\t\tif decodeErr != nil || config.Width < 1 || config.Height < 1 || config.Width > 16384 || config.Height > 16384 ||","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/api/system.go#L337-L373","documentation":"Returned by normalizeCustomEmojiData when the input byte slice is zero-length. This means the upload/url-download produced an empty body: a 200 with empty content, a zero-byte file, or a LimitReader that read nothing.","triggerScenarios":"POST /api/system/addCustomEmoji with an empty file, or a url whose server returns 200 with a zero-length body (e.g. a CDN edge returning an empty object, or a misconfigured object store).","commonSituations":"User selected a 0-byte file by mistake; the file was truncated during a prior sync; the source URL is an empty S3 object; git-LFS pointer file not yet resolved.","solutions":["Check the source file size locally (ls -l / stat) before uploading.","If using url, curl -I the URL and confirm Content-Length is non-zero and matches an image.","Re-export or re-download the original asset and retry."],"exampleFix":"// before\n// uploading a 0-byte file -> 'custom emoji file must not be empty'\n\n// after: guard before the API call\nif fi, e := os.Stat(path); e != nil || fi.Size() == 0 {\n    return errors.New(\"emoji file is missing or empty\")\n}","handlingStrategy":"validation","validationCode":"if len(data) == 0 { return errors.New(\"emoji data is empty before upload\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Stat the file before upload and reject zero-byte files.","For url, curl -I and confirm Content-Length > 0.","Watch for git-LFS pointer files that are tiny but not the real asset."],"tags":["validation","emoji","empty-file","api"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}