{"record":{"id":"0d95e048051bf6ab","repo":"siyuan-note/siyuan","slug":"parent-block-not-found-s","errorCode":null,"errorMessage":"parent block not found: %s","messagePattern":"parent block not found: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/treenode/blocktree.go","lineNumber":542,"sourceCode":"\n// IsContainerType 按主类型缩写判断块是否为容器块（可合法接收子块）。\n// 入参 abbrType 对应 BlockTree.Type（如 \"d\"/\"h\"/\"p\"），由 TypeAbbr 写入。\nfunc IsContainerType(abbrType string) bool {\n\tswitch abbrType {\n\tcase \"d\", \"b\", \"l\", \"i\", \"s\", \"callout\":\n\t\treturn true\n\t}\n\treturn false\n}\n\n// CheckContainerParent 校验 parentID 指向的块是否允许接收子块。\n// 仅在“通过 parentID 定位插入/移动目标”（即不依赖 previousID/nextID）的场景下调用，\n// 因为一旦带 previousID/nextID，事务层走的是兄弟级 InsertAfter/InsertBefore，天然合法。\n// 返回 nil 表示合法；返回 error 时调用方应拒绝本次操作。\nfunc CheckContainerParent(parentID string) error {\n\tbt := GetBlockTree(parentID)\n\tif nil == bt {\n\t\treturn fmt.Errorf(\"parent block not found: %s\", parentID)\n\t}\n\tif IsContainerType(bt.Type) {\n\t\treturn nil\n\t}\n\tif \"h\" == bt.Type {\n\t\t// 标题是叶子块，其“子内容”在数据结构上实为后续兄弟节点（由 HeadingChildren 按层级推算）。\n\t\t// 把块挂成标题的 AST 子节点属于非法嵌套，应改用 previousID 定位。\n\t\treturn fmt.Errorf(\"heading [%s] is a leaf block and cannot have children; to place a block below this heading, pass previousID=<heading id> or previousID=<last block below the heading> instead of parentID\", parentID)\n\t}\n\treturn fmt.Errorf(\"block [%s] type %q is a leaf block and cannot have children; use previousID to place the block as its sibling instead\", parentID, bt.Type)\n}\n\n// CheckListItemNesting 校验 parentID 和 childID 是否形成“列表项直含列表项”的非法嵌套。\n// 嵌套列表的正确结构是 ListItem > List > ListItem，列表项不能直接作为另一个列表项的子块。\n// 仅在 move 场景调用（源和目标类型均已知）。\nfunc CheckListItemNesting(parentID, childID string) error {\n\tparentBt := GetBlockTree(parentID)\n\tchildBt := GetBlockTree(childID)","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/treenode/blocktree.go#L524-L560","documentation":"Returned by CheckContainerParent in kernel/treenode/blocktree.go when GetBlockTree(parentID) returns nil — no blocktree row exists for the given parentID. CheckContainerParent is the guard used by insert/move block operations that locate the target by parentID (without previousID/nextID).","triggerScenarios":"Calling a block insert or move API with a parentID that does not correspond to any indexed block. CheckContainerParent is invoked from HTTP api/block_op.go, MCP tools/block.go, cli/cmd/block.go, and tools/asset.go whenever the operation is parentID-based.","commonSituations":"Stale or copied-wrong block ID; the parent block was deleted; the parent lives in an encrypted notebook that is currently locked (its blocktree db returns nil); leading/trailing whitespace in the ID.","solutions":["Verify the parentID exists with treenode.GetBlockTree(parentID) (or ExistBlockTree) before calling the insert/move API.","If the parent is in an encrypted notebook, unlock the notebook first so its blocktree db is opened.","Re-index the notebook (kernel index rebuild) if you suspect the blocktree row is missing for a block that does exist on disk."],"exampleFix":"// before\nerr := treenode.CheckContainerParent(parentID) // -> parent block not found\n\n// after\nif treenode.GetBlockTree(parentID) == nil {\n    return fmt.Errorf(\"parent %q does not exist (deleted, stale, or locked encrypted box)\", parentID)\n}\nerr := treenode.CheckContainerParent(parentID)","handlingStrategy":"validation","validationCode":"// Resolve the parent before calling the insert/move API.\nif treenode.GetBlockTree(parentID) == nil {\n    return fmt.Errorf(\"parent %q not found; deleted, stale, or locked encrypted notebook\", parentID)\n}\nreturn treenode.CheckContainerParent(parentID)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always confirm the parent id with GetBlockTree/ExistBlockTree before parentID-based insert or move.","Trim whitespace from IDs received from user input.","If the parent lives in an encrypted notebook, unlock the notebook first."],"tags":["block","blocktree","parent","validation","insert"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}