{"record":{"id":"a19632856e32a71c","repo":"siyuan-note/siyuan","slug":"invalid-box-id-s","errorCode":null,"errorMessage":"invalid box ID [%s]","messagePattern":"invalid box ID \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/box_doc.go","lineNumber":130,"sourceCode":"\tvar bt *treenode.BlockTree\n\tif \"\" == boxID {\n\t\tbt = treenode.GetBlockTree(id)\n\t} else {\n\t\tbt = treenode.GetBlockTreeInBox(id, boxID)\n\t}\n\treturn nil != bt && IsBoxDoc(bt.BoxID, bt.RootID)\n}\n\nfunc EnsureBoxDoc(boxID string) (boxDocID string, err error) {\n\tcreateDocLock.Lock()\n\tdefer createDocLock.Unlock()\n\treturn ensureBoxDoc0(boxID)\n}\n\n// ensureBoxDoc0 的调用方必须持有 createDocLock。\nfunc ensureBoxDoc0(boxID string) (boxDocID string, err error) {\n\tif !ast.IsNodeIDPattern(boxID) {\n\t\treturn \"\", fmt.Errorf(\"invalid box ID [%s]\", boxID)\n\t}\n\n\tbox := Conf.GetBox(boxID)\n\tif nil == box {\n\t\treturn \"\", ErrBoxNotFound\n\t}\n\n\tif !IsBoxDocEnabled() {\n\t\treturn\n\t}\n\tboxDocID = boxID\n\n\tboxDocID, err = findBoxDoc(box)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tcreated, changed := false, false\n\tif \"\" == boxDocID {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/box_doc.go#L112-L148","documentation":"ensureBoxDoc0 validates that the given box ID looks like a SiYuan node ID (32-char lowercase hex, per ast.IsNodeIDPattern) before creating or locating the box's hidden document. The box ID here is a notebook identifier that must follow the node ID format; anything else is rejected immediately with this formatted message echoing the offending value. It is thrown before any disk or database lookups.","triggerScenarios":"Calling model.EnsureBoxDoc or createBox with a boxID string that is empty, contains invalid characters, or is otherwise not a valid ast node ID pattern.","commonSituations":"Scripts or plugins constructing a notebook ID manually instead of using one returned by listNotebooks; truncated or uppercased IDs from external tools; passing a human-readable notebook name where an ID is expected.","solutions":["Pass a valid notebook ID (32 lowercase hex characters) obtained from Conf.GetBox / listNotebooks output","Trim whitespace and normalize case of the box ID before calling","If generating a new ID, use ast.NewNodeID() rather than a custom string"],"exampleFix":"// before\nEnsureBoxDoc(\"my notebook\")\n// after\nbox := Conf.GetBox(\"20240101120000-abcdef0123456789\")\nif box != nil {\n    EnsureBoxDoc(box.ID)\n}","handlingStrategy":"validation","validationCode":"function isValidBoxID(id) {\n  return typeof id === \"string\" && /^[0-9]{14}-[0-9a-z]{16}$/.test(id);\n}\nif (!isValidBoxID(boxID)) throw new Error(\"refusing to call EnsureBoxDoc with invalid ID\");","typeGuard":"const isBoxID = (v: unknown): v is string => typeof v === \"string\" && /^[0-9]{14}-[0-9a-z]{16}$/.test(v);","tryCatchPattern":null,"preventionTips":["Always take box IDs from Conf.GetBox or notebook listing output, never hand-build them","Trim and lowercase IDs coming from external scripts before use","Keep a single helper that validates node-ID format before any kernel call"],"tags":["validation","notebook","identifier"],"backgroundTag":"invalid-identifier-format","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}