siyuan-note/siyuan · error
block is not a task list item
Error message
block is not a task list item
What it means
The updateTaskListItemMarker endpoint only operates on task list items (list nodes with ListData.Typ == 3). buildUpdatedTaskListItemBlockDOM found the node in the tree, but it is a regular ordered/bullet list item, not a task item, so it refuses to apply a checkbox marker. This is a type guard, not a state problem.
Solutions
- Filter your block query to task list items only, e.g. subtype='t' on l blocks (SELECT id FROM blocks WHERE type='l' AND subtype='t')
- Check the returned block type/subtype with /api/query/sql before calling the marker API
- Convert the target to a task list item in the editor first, or use a generic block update operation instead of the marker-specific endpoint
- If you need to toggle markers in bulk, verify each item's subtype in the batch loop and skip non-task items
Example fix
// before
const blocks = await sql("SELECT id FROM blocks WHERE type='l' AND content LIKE '%TODO%'");
// after
const blocks = await sql("SELECT id FROM blocks WHERE type='l' AND subtype='t' AND content LIKE '%TODO%'"); Defensive patterns
Strategy: validation
Validate before calling
const rows = await fetchPost('/api/query/sql', { stmt: `SELECT subtype FROM blocks WHERE id='${id}'` });
if (!rows.length || rows[0].subtype !== 't') throw new Error(`block ${id} is not a task list item`); Type guard
const isTaskListItem = (b) => !!b && b.type === 'l' && b.subtype === 't';
Try / catch
try {
return await fetchPost('/api/block/updateTaskListItemMarker', { id, marker });
} catch (e) {
if (typeof e?.msg === 'string' && e.msg.includes('not a task list item')) {
console.warn(`Skipping non-task list item ${id}`);
return null; // skip, don't crash the batch
}
throw e;
} Prevention
- Filter SQL queries with subtype='t' when targeting task items
- Check block.type/subtype before calling marker-specific APIs
- Re-validate item types after content edits that can convert list kinds
When it happens
Trigger: Passing the ID of a bullet list item (ListData.Typ == 0), an ordered list item (Typ == 1), or any non-list node (paragraph, heading, etc.) to POST /api/block/updateTaskListItemMarker or batchUpdateTaskListItemMarker.
Common situations: A plugin queries blocks WHERE type='l' but the matching item is a plain bullet list; UI selection grabbed the wrong node; content was converted from task item to bullet item by an earlier edit so a cached ID now points at a non-task item.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- block not found
- task list item marker not found
- block is not a list item
- block not found or its encrypted notebook is locked
- setting or removing [data-task] attribute is not allowed…
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/68fd965df3880661.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/api/block_op.go:55
return "", errors.New("get block failed: " + err.Error())
}
if "NodeListItem" != block.Type {
return "", errors.New("block is not a list item")
}
tree, err := filesys.LoadTree(block.Box, block.Path, luteEngine)
if err != nil {
return "", errors.New("load tree failed: " + err.Error())
}
li := treenode.GetNodeInTree(tree, id)
if li == nil {
return "", errors.New("block not found")
}
if 3 != li.ListData.Typ {
return "", errors.New("block is not a task list item")
}
if 1 != len(marker) {
return "", errors.New("task list item marker length should be 1")
}
liMarker := marker[0]
if '[' == liMarker || ']' == liMarker {
return "", errors.New("task list item marker can not be [ or ]")
}
markerNode := li.ChildByType(ast.NodeTaskListItemMarker)
if nil == markerNode {
return "", errors.New("task list item marker not found")
}
markerNode.TaskListItemMarker = liMarker
markerNode.TaskListItemChecked = ' ' != markerNode.TaskListItemMarkerView on GitHub (pinned to 9f775e8a12)