{"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":"error","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/9f775e8a12daef8255556097396f9b2739078892/kernel/model/block_update.go#L231-L267","documentation":"When the old block being updated is a list item and the new payload parses as a whole list (NodeList), resolveBlockUpdateNode tries to unwrap the first list item from the new list to keep the update type-compatible. If that list contains no list item (or the first content block is not a list item), the update cannot proceed and this error is returned.","triggerScenarios":"Updating a list-item block (oldNode.Type == NodeListItem) with new data that parses to a NodeList whose first content block is not a NodeListItem — e.g. replacing a list item with a list containing only nested non-item content, or a malformed list structure after BlockDOM round-trip. Covered by TestResolveSuperBlockListItemAfterBlockDOMRoundTrip.","commonSituations":"API updates replacing a list item's content with data that changed list structure (item converted to a code block inside the list); DOM round-trips that dropped the list-item wrapper; plugins re-authoring list blocks.","solutions":["Ensure the replacement payload for a list item starts with a proper list item node","If the block should stop being a list item, update the parent list block instead of the item, or delete and re-insert","Check that DOM serialization preserved the <ul>/<li> (or data-type NodeListItem) structure"],"exampleFix":"// before\ndataType: \"dom\", data: \"<ul>...</ul>\" // replacing a list item with an empty/invalid list\n// after\ndataType: \"dom\", data: '<ul><li data-node-id=\"...\" data-type=\"NodeListItem\"><div class=\"protyle-action\">...</div><div data-node-id=\"...\" data-type=\"NodeParagraph\" class=\"p\">...</div></li></ul>'","handlingStrategy":"validation","validationCode":"// when updating a list item, the payload must contain a list item as first content block\nif (oldNodeType === \"NodeListItem\" && !/<li[^>]*data-type=\"NodeListItem\"/i.test(payload.data)) {\n  throw new Error(\"updating a list item requires NodeListItem content\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await updateBlock(payload);\n} catch (e) {\n  if (String(e.msg) === \"list block has no list item\") {\n    // unwrap or restructure the list payload before retrying\n  }\n}","preventionTips":["Preserve <li>/NodeListItem structure when round-tripping list DOM","Update the parent list block when the item type should change","Prefer delete + insert for structural list changes"],"tags":["list","block-update","parse","structure-mismatch"],"backgroundTag":"incompatible-source-type","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}