siyuan-note/siyuan · warning

list block has no list item

Error message

list block has no list item

What it means

Returned by resolveBlockUpdateNode. The old node is a NodeListItem and the updated node resolved to a NodeList (the user submitted list markup), so the code looks for a NodeListItem inside the list via firstContentBlock. If none exists or it isn't a NodeListItem, the list is malformed - a list with no list item cannot replace a list item.

Source

Thrown at kernel/model/block_update.go:249

	updatedNode.Unlink()
	root := &ast.Node{Type: ast.NodeDocument}
	root.AppendChild(updatedNode)
	ret = &parse.Tree{
		Root:    root,
		Context: &parse.Context{ParseOption: luteEngine.ParseOptions},
	}
	return
}

func resolveBlockUpdateNode(oldNode, root *ast.Node) (updatedNode *ast.Node, err error) {
	updatedNode = firstContentBlock(root)
	if nil == updatedNode {
		return nil, errors.New("parse tree failed")
	}
	if ast.NodeListItem == oldNode.Type && ast.NodeList == updatedNode.Type {
		listItem := firstContentBlock(updatedNode)
		if nil == listItem || ast.NodeListItem != listItem.Type {
			return nil, errors.New("list block has no list item")
		}
		updatedNode = listItem
	}
	return
}

// pinDescendantBlockIDs 把旧块子树中对应位置的子块 ID 钉回新块,避免更新容器块时 Lute 重新生成
// 子块 ID,导致指向子块的块引用、反链、闪卡等失效。
// 匹配规则:按同级内容块顺序对齐,类型一致才沿用旧 ID;类型不一致时向后查找同类型的旧子块重新
// 对齐,这样插入或删除子块后其余子块仍能匹配上旧 ID,新增的子块保持新生成的 ID。
func pinDescendantBlockIDs(oldNode, updatedNode *ast.Node) {
	oldChildren := blockChildrenOf(oldNode)
	oldIndex := 0
	for _, newChild := range blockChildrenOf(updatedNode) {
		if oldIndex >= len(oldChildren) {
			break
		}
		if oldChildren[oldIndex].Type != newChild.Type {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Ensure the list input contains at least one non-empty list item (e.g. "- item").
  2. Validate the parsed tree has a NodeListItem child before submitting when updating a list item.
  3. If the intent is to delete the item, call the delete API instead of submitting an empty list.

Example fix

// before
// input.Data = "- "

// after
// input.Data = "- actual content"
Defensive patterns

Strategy: validation

Validate before calling

// when updating a list item, ensure the markdown has at least one item with content
if oldNodeIsListItem && !regexp.MustCompile(`^[-*+]\s+\S`).MatchString(strings.TrimSpace(input.Data)) {
    return errors.New("list input has no list item with content")
}

Prevention

When it happens

Trigger: Updating a list item with markdown/dom that produces an empty list (e.g. "- " with no content) or a list whose only child is another nested list; submitting "* " alone.

Common situations: Client sending placeholder list syntax; markdown stripped of its item content in transit; Lute producing a NodeListBox without a child item from degenerate input.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/8743ffdf3bf29bae. Report an issue: GitHub.