{"record":{"id":"18fcb60917735226","repo":"siyuan-note/siyuan","slug":"invalid-id","errorCode":null,"errorMessage":"invalid id","messagePattern":"invalid id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/tree.go","lineNumber":208,"sourceCode":"\t\t}\n\t}\n\n\tret, err = dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)\n\tif err != nil {\n\t\tlogging.LogErrorf(\"parse json to tree [%s] failed: %s\", localPath, err)\n\t\treturn\n\t}\n\treturn\n}\n\nvar (\n\tErrBoxNotFound   = errors.New(\"notebook not found\")\n\tErrBoxClosed     = errors.New(\"notebook closed\")\n\tErrBlockNotFound = errors.New(\"block not found\")\n\tErrTreeNotFound  = errors.New(\"tree not found\")\n\tErrIndexing      = errors.New(\"indexing\")\n\tErrBoxUnindexed  = errors.New(\"notebook unindexed\")\n\tErrInvalidID     = errors.New(\"invalid id\")\n)\n\nfunc LoadTreeByBlockIDWithReindex(id string) (ret *parse.Tree, err error) {\n\treturn LoadTreeByBlockIDWithReindexInBox(id, \"\")\n}\n\n// LoadTreeByBlockIDWithReindexInBox 与 LoadTreeByBlockIDWithReindex 一致，但按 boxID 路由 blocktree 查询。\nfunc LoadTreeByBlockIDWithReindexInBox(id, boxID string) (ret *parse.Tree, err error) {\n\tif \"\" == id {\n\t\tlogging.LogWarnf(\"block id is empty\")\n\t\treturn nil, ErrTreeNotFound\n\t}\n\n\tbt := treenode.GetBlockTreeInBox(id, boxID)\n\tif nil == bt && \"\" == boxID {\n\t\t// boxID 未知时（如通用打开入口），遍历所有已打开的加密笔记本查找\n\t\tfor _, encBoxID := range treenode.GetOpenedEncryptedBoxIDs() {\n\t\t\tif encBT := treenode.GetBlockTreeInBox(id, encBoxID); nil != encBT {","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/tree.go#L190-L226","documentation":"Sentinel error model.ErrInvalidID (kernel/model/tree.go:208). It flags identifiers that fail format validation rather than lookup - the attribute view render path returns it for malformed av/block ids (attribute_view_render.go:394, 1047, 1083, 1124, 1189), and kernel tests assert it for invalid attribute view inputs. Unlike ErrBlockNotFound/ErrTreeNotFound, the id never gets as far as a blocktree lookup: its shape is wrong.","triggerScenarios":"Feeding a self-invented, truncated, or wrongly-typed id into attribute view render/getAttributeView* APIs; passing an empty or whitespace id where a kernel-issued id is required.","commonSituations":"Plugins generating their own ids instead of using ids the kernel returned; copy-paste truncation; substituting a document path or av name where an id belongs.","solutions":["Use ids exactly as returned by the kernel (block info queries, av search/list endpoints)","Validate the id shape client-side before calling (see type guard)","Match with errors.Is(err, model.ErrInvalidID) to distinguish 'bad format' from 'not found' in error handling"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Reject malformed ids client-side before attribute-view calls:\nvar idRe = regexp.MustCompile(`^[0-9]{14}-[a-z0-9]{7}$`)\nif !idRe.MatchString(avID) {\n\treturn fmt.Errorf(\"invalid id format: %s\", avID)\n}","typeGuard":"func isValidNodeID(id string) bool {\n\t// SiYuan node ids: 14-digit timestamp + '-' + 7-char suffix\n\tmatched, _ := regexp.MatchString(`^[0-9]{14}-[a-z0-9]{7}$`, id)\n\treturn matched\n}","tryCatchPattern":"if err := renderAV(avID); err != nil {\n\tif errors.Is(err, model.ErrInvalidID) {\n\t\t// format problem - fix the id source, retrying will not help\n\t}\n}","preventionTips":["Only use ids returned by kernel APIs (block info, av list/search)","Validate id shape at input boundaries (HTTP params, plugin settings)","Log the offending id when ErrInvalidID fires to find which producer created it"],"tags":["validation","sentinel-error","id-format","attribute-view"],"backgroundTag":"invalid-identifier-format","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}