{"record":{"id":"8743ffdf3bf29bae","repo":"siyuan-note/siyuan","slug":"list-block-has-no-list-item","errorCode":null,"errorMessage":"list block has no list item","messagePattern":"list block has no list item","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/block_update.go","lineNumber":249,"sourceCode":"\tupdatedNode.Unlink()\n\troot := &ast.Node{Type: ast.NodeDocument}\n\troot.AppendChild(updatedNode)\n\tret = &parse.Tree{\n\t\tRoot:    root,\n\t\tContext: &parse.Context{ParseOption: luteEngine.ParseOptions},\n\t}\n\treturn\n}\n\nfunc resolveBlockUpdateNode(oldNode, root *ast.Node) (updatedNode *ast.Node, err error) {\n\tupdatedNode = firstContentBlock(root)\n\tif nil == updatedNode {\n\t\treturn nil, errors.New(\"parse tree failed\")\n\t}\n\tif ast.NodeListItem == oldNode.Type && ast.NodeList == updatedNode.Type {\n\t\tlistItem := firstContentBlock(updatedNode)\n\t\tif nil == listItem || ast.NodeListItem != listItem.Type {\n\t\t\treturn nil, errors.New(\"list block has no list item\")\n\t\t}\n\t\tupdatedNode = listItem\n\t}\n\treturn\n}\n\n// pinDescendantBlockIDs 把旧块子树中对应位置的子块 ID 钉回新块，避免更新容器块时 Lute 重新生成\n// 子块 ID，导致指向子块的块引用、反链、闪卡等失效。\n// 匹配规则：按同级内容块顺序对齐，类型一致才沿用旧 ID；类型不一致时向后查找同类型的旧子块重新\n// 对齐，这样插入或删除子块后其余子块仍能匹配上旧 ID，新增的子块保持新生成的 ID。\nfunc pinDescendantBlockIDs(oldNode, updatedNode *ast.Node) {\n\toldChildren := blockChildrenOf(oldNode)\n\toldIndex := 0\n\tfor _, newChild := range blockChildrenOf(updatedNode) {\n\t\tif oldIndex >= len(oldChildren) {\n\t\t\tbreak\n\t\t}\n\t\tif oldChildren[oldIndex].Type != newChild.Type {","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/block_update.go#L231-L267","documentation":"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.","triggerScenarios":"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.","commonSituations":"Client sending placeholder list syntax; markdown stripped of its item content in transit; Lute producing a NodeListBox without a child item from degenerate input.","solutions":["Ensure the list input contains at least one non-empty list item (e.g. \"- item\").","Validate the parsed tree has a NodeListItem child before submitting when updating a list item.","If the intent is to delete the item, call the delete API instead of submitting an empty list."],"exampleFix":"// before\n// input.Data = \"- \"\n\n// after\n// input.Data = \"- actual content\"","handlingStrategy":"validation","validationCode":"// when updating a list item, ensure the markdown has at least one item with content\nif oldNodeIsListItem && !regexp.MustCompile(`^[-*+]\\s+\\S`).MatchString(strings.TrimSpace(input.Data)) {\n    return errors.New(\"list input has no list item with content\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure list input has at least one non-empty list item.","Use the delete API when the intent is to remove the item, not submit an empty list.","Validate parsed tree has a NodeListItem child before submitting list updates."],"tags":["block-update","list","validation","lute"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}