{"record":{"id":"289580e315e1092e","repo":"siyuan-note/siyuan","slug":"block-is-not-a-list-item","errorCode":null,"errorMessage":"block is not a list item","messagePattern":"block is not a list item","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/block_op.go","lineNumber":41,"sourceCode":"\t\"github.com/88250/gulu\"\n\t\"github.com/88250/lute\"\n\t\"github.com/88250/lute/ast\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/siyuan-note/siyuan/kernel/apicontract\"\n\t\"github.com/siyuan-note/siyuan/kernel/filesys\"\n\t\"github.com/siyuan-note/siyuan/kernel/model\"\n\t\"github.com/siyuan-note/siyuan/kernel/treenode\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\nfunc buildUpdatedTaskListItemBlockDOM(id, marker string, luteEngine *lute.Lute) (data string, err error) {\n\tblock, err := model.GetBlock(id, nil)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"get block failed: \" + err.Error())\n\t}\n\n\tif \"NodeListItem\" != block.Type {\n\t\treturn \"\", errors.New(\"block is not a list item\")\n\t}\n\n\ttree, err := filesys.LoadTree(block.Box, block.Path, luteEngine)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"load tree failed: \" + err.Error())\n\t}\n\n\tli := treenode.GetNodeInTree(tree, id)\n\tif li == nil {\n\t\treturn \"\", errors.New(\"block not found\")\n\t}\n\n\tif 3 != li.ListData.Typ {\n\t\treturn \"\", errors.New(\"block is not a task list item\")\n\t}\n\n\tif 1 != len(marker) {\n\t\treturn \"\", errors.New(\"task list item marker length should be 1\")","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/api/block_op.go#L23-L59","documentation":"After fetching the block, buildUpdatedTaskListItemBlockDOM requires block.Type == \"NodeListItem\"; any other block type (paragraph, heading, etc.) makes the task-list-item DOM update meaningless, so it fails with this error.","triggerScenarios":"Calling the task list item update operation with a block ID that resolves to a non-list-item block, e.g. a paragraph inside a task item rather than the item itself.","commonSituations":"Client grabbing the inner text block's ID instead of the li block ID; document structure changed (task marker removed) so the block is now a plain paragraph; plugin assumptions about older content.","solutions":["Pass the NodeListItem block's ID, not a child node's ID","Re-read the block's type via /api/filetree/getDoc or getBlockInfo and route non-item blocks elsewhere","If the item lost its task marker, re-apply the task formatting first"],"exampleFix":"// before\nupdateTaskItem(childParagraphId)\n// after\nconst parentId = block.parentID // NodeListItem\nupdateTaskItem(parentId)","handlingStrategy":"validation","validationCode":"const info = await fetchPost('/api/block/getBlockInfo', {id});\nif (info.data.type !== 'NodeListItem') throw new Error('not a list item');","typeGuard":"const isListItem = (b) => b?.type === 'NodeListItem';","tryCatchPattern":"try { await updateTaskItem(id, marker) } catch (e) { if (String(e).includes('not a list item')) await redirectToGenericBlockUpdate(id) }","preventionTips":["Resolve the closest NodeListItem ancestor before task-specific calls","Re-read block type after any structural transaction","Centralize block-type assertions in one client helper"],"tags":["blocks","type-mismatch","api"],"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"}