siyuan-note/siyuan · error
block [%s] type %q is a leaf block and cannot have children;
Error message
block [%s] type %q is a leaf block and cannot have children; use previousID to place the block as its sibling instead
What it means
Returned by CheckContainerParent when the resolved block is a non-heading leaf type (IsContainerType false, Type != "h") — e.g. a paragraph, code block, math block, table, etc. Such blocks cannot have AST children; content must be placed as a sibling.
Source
Thrown at kernel/treenode/blocktree.go:552
// CheckContainerParent 校验 parentID 指向的块是否允许接收子块。
// 仅在“通过 parentID 定位插入/移动目标”(即不依赖 previousID/nextID)的场景下调用,
// 因为一旦带 previousID/nextID,事务层走的是兄弟级 InsertAfter/InsertBefore,天然合法。
// 返回 nil 表示合法;返回 error 时调用方应拒绝本次操作。
func CheckContainerParent(parentID string) error {
bt := GetBlockTree(parentID)
if nil == bt {
return fmt.Errorf("parent block not found: %s", parentID)
}
if IsContainerType(bt.Type) {
return nil
}
if "h" == bt.Type {
// 标题是叶子块,其“子内容”在数据结构上实为后续兄弟节点(由 HeadingChildren 按层级推算)。
// 把块挂成标题的 AST 子节点属于非法嵌套,应改用 previousID 定位。
return 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)
}
return 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)
}
// CheckListItemNesting 校验 parentID 和 childID 是否形成“列表项直含列表项”的非法嵌套。
// 嵌套列表的正确结构是 ListItem > List > ListItem,列表项不能直接作为另一个列表项的子块。
// 仅在 move 场景调用(源和目标类型均已知)。
func CheckListItemNesting(parentID, childID string) error {
parentBt := GetBlockTree(parentID)
childBt := GetBlockTree(childID)
if nil == parentBt || nil == childBt {
return nil // 查不到就放行,不阻塞未知场景
}
if "i" == parentBt.Type && "i" == childBt.Type {
return fmt.Errorf("a list-item cannot directly contain another list-item; to nest, first create a list (NodeList) under the outer list-item, then add the inner list-items to that list")
}
return nil
}
func SetBlockTreePath(tree *parse.Tree) {View on GitHub (pinned to 251596fc0d)
Solutions
- Switch the request from parentID to previousID (or nextID) so the block is placed as a sibling.
- If you intended nesting, target a real container (document, superblock, list, list-item, blockquote) instead of the leaf.
- Check the block's Type via GetBlockTree before deciding parentID vs previousID.
Example fix
// before
InsertBlock(parentID: paragraphID, data: block) // -> leaf block cannot have children
// after
bt := treenode.GetBlockTree(paragraphID)
if treenode.IsContainerType(bt.Type) {
InsertBlock(parentID: paragraphID, data: block)
} else {
InsertBlock(previousID: paragraphID, data: block)
} Defensive patterns
Strategy: validation
Validate before calling
// Choose parent vs sibling placement based on whether the target is a container.
bt := treenode.GetBlockTree(parentID)
if bt == nil {
return fmt.Errorf("parent %q not found", parentID)
}
if !treenode.IsContainerType(bt.Type) {
return fmt.Errorf("block %s is a leaf (%s); use previousID to place as sibling", parentID, bt.Type)
}
return nil Prevention
- Resolve the target block's Type before deciding between parentID and previousID.
- Only document, superblock, list, list-item, and blockquote blocks accept children.
- Default to previousID when the target type is unknown.
When it happens
Trigger: Calling a parentID-based insert or move with parentID set to any leaf block (paragraph, code, blockquote-content-as-leaf, etc.). Guard runs in api/block_op.go, MCP tools/block.go, cli/cmd/block.go insert/move paths.
Common situations: Treating a paragraph as a container; moving a block 'into' a code or math block via parentID; misreading the block type when constructing the request.
Related errors
- heading [%s] is a leaf block and cannot have children; to pl
- a list-item cannot directly contain another list-item; to ne
- --id is required
- block [%s] is not an instance of attribute view [%s]
- block not found
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/d405a82bc960343c.
Report an issue: GitHub.