siyuan-note/siyuan · error
a list-item cannot directly contain another list-item; to ne
Error message
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
What it means
Returned by CheckListItemNesting in kernel/treenode/blocktree.go when both the resolved parent and child blocks have Type "i" (list item). SiYuan's correct nested-list structure is ListItem > List > ListItem; a list item may not be a direct AST child of another list item. CheckListItemNesting is only called in the move scenario where both source and target types are known.
Source
Thrown at kernel/treenode/blocktree.go:565
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) {
RemoveBlockTreesByRootID(tree.Box, tree.ID)
IndexBlockTree(tree)
}
func RemoveBlockTreesByRootID(boxID, rootID string) {
sqlStmt := "DELETE FROM blocktrees WHERE root_id = ?"
_, err := execForBox(boxID, sqlStmt, rootID)
if err != nil {
logging.LogErrorf("sql exec [%s] failed: %s", sqlStmt, err)
return
}
}
View on GitHub (pinned to 251596fc0d)
Solutions
- Create or locate the NodeList child under the outer list-item first, then move the inner list-item under that list.
- Use the editor's drag/drop (which builds the intermediate List) instead of a raw parentID move.
- If no list exists under the outer item, insert one (NodeList) before moving the inner item.
Example fix
// before MoveBlock(parentID: outerListItemID, id: innerListItemID) // -> cannot directly contain // after: nest via an intermediate NodeList listID := createListUnderListItem(outerListItemID) MoveBlock(parentID: listID, id: innerListItemID)
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check list-item-to-list-item moves before calling the move API.
parent := treenode.GetBlockTree(parentID)
child := treenode.GetBlockTree(childID)
if parent != nil && child != nil && parent.Type == "i" && child.Type == "i" {
return fmt.Errorf("nest via an intermediate NodeList under list-item %s", parentID)
}
return nil Prevention
- Remember the list nesting rule: ListItem > List > ListItem, never ListItem > ListItem.
- Ensure an intermediate NodeList exists (or create one) before moving an item into another item.
- Prefer the editor drag/drop path for list nesting; it creates the intermediate List for you.
When it happens
Trigger: A move-block operation whose target parent (parentID) is a list item and whose moved block is also a list item. Invoked from api/block_op.go, MCP tools/block.go, and cli/cmd/block.go move handlers.
Common situations: Dragging a list item into another list item expecting sub-nesting, without the intermediate List node; programmatic re-parenting of list items.
Related errors
- heading [%s] is a leaf block and cannot have children; to pl
- block [%s] type %q is a leaf block and cannot have children;
- --id is required
- block [%s] is not an instance of attribute view [%s]
- list block has no list item
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/259402b1f38ab57e.
Report an issue: GitHub.