{"record":{"id":"2c943b71acc859d0","repo":"siyuan-note/siyuan","slug":"invalid-custom-font-id","errorCode":null,"errorMessage":"invalid custom font ID","messagePattern":"invalid custom font ID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/custom_font.go","lineNumber":169,"sourceCode":"\t} else if !os.IsNotExist(statErr) {\n\t\treturn nil, false, statErr\n\t}\n\n\tif err = os.Rename(tempPath, targetPath); err != nil {\n\t\treturn nil, false, err\n\t}\n\tif err = os.Chmod(targetPath, 0644); err != nil {\n\t\t_ = os.Remove(targetPath)\n\t\treturn nil, false, err\n\t}\n\n\tcustomFontsLoaded = false\n\treturn newCustomFont(id, targetPath, font), true, nil\n}\n\nfunc RemoveCustomFont(id string) (*CustomFont, error) {\n\tif !validCustomFontID(id) {\n\t\treturn nil, errors.New(\"invalid custom font ID\")\n\t}\n\n\tcustomFontsLock.Lock()\n\tdefer customFontsLock.Unlock()\n\n\tloadCustomFontsLocked()\n\tfor _, font := range customFonts {\n\t\tif font.ID != id {\n\t\t\tcontinue\n\t\t}\n\t\tif err := os.Remove(font.path); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcustomFontsLoaded = false\n\t\treturn cloneCustomFont(font), nil\n\t}\n\treturn nil, os.ErrNotExist\n}","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/custom_font.go#L151-L187","documentation":"Returned by RemoveCustomFont when the supplied id fails validCustomFontID. A valid ID is exactly 64 lowercase hex characters (sha256.Size*2) that hex-decode to 32 bytes — i.e. the sha256 hash produced at install time. Anything else is rejected before touching the font directory.","triggerScenarios":"Calling util.RemoveCustomFont(id) (or the matching kernel API) with an id that is the wrong length, contains uppercase letters or non-hex characters, or is otherwise not a sha256 hex digest.","commonSituations":"A client passed the font family name, display name, URL slug, or an index instead of the ID; the ID was truncated or had a leading '/custom-fonts/' prefix appended; a stale client from before IDs were introduced.","solutions":["Pass the exact CustomFont.ID returned by LoadCustomFonts (64-char lowercase hex), not the family/display name or URL.","Strip any '/custom-fonts/' prefix or file extension before calling RemoveCustomFont.","If the client only has a display name, resolve it to an ID via LoadCustomFonts first."],"exampleFix":"// before\nutil.RemoveCustomFont(displayName)\n\n// after\nvar id string\nfor _, f := range util.LoadCustomFonts() {\n    if f.DisplayName == displayName {\n        id = f.ID\n        break\n    }\n}\nif id == \"\" {\n    return errors.New(\"font not found\")\n}\nutil.RemoveCustomFont(id)","handlingStrategy":"validation","validationCode":"var fontIDRE = regexp.MustCompile(`^[0-9a-f]{64}$`)\nfunc isValidFontID(id string) bool { return fontIDRE.MatchString(id) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass the exact CustomFont.ID (64-char lowercase hex) from LoadCustomFonts.","Resolve display names to IDs at the client before calling remove."],"tags":["fonts","validation","api"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}