siyuan-note/siyuan · error
block not found
Error message
block not found
What it means
SiYuan's task-list-item update API resolves the block by ID within its loaded document tree via treenode.GetNodeInTree. Although model.GetBlock succeeded earlier in buildUpdatedTaskListItemBlockDOM, the node cannot be located in the freshly parsed AST, so the function aborts with "block not found". This indicates the SQL index and the on-disk .sy tree are out of sync, or the tree changed between the two lookups.
Solutions
- Verify the block still exists with /api/filetree/getDoc (or /api/query/sql SELECT * FROM blocks WHERE id='...'), then re-fetch a fresh ID before retrying the marker update
- Rebuild the index (Settings - About - Rebuild index, or /api/system/reindex) if the blocks table and .sy files disagree
- Confirm the document containing the block was not deleted/moved by a concurrent client or sync; re-open the doc to get current IDs
- Re-run the update in a retry after a short delay if a sync/index queue was mid-flight
Example fix
// before: cached id may be stale
await fetchPost('/api/block/updateTaskListItemMarker', { id: cachedId, marker: 'x' });
// after: re-resolve the id from a fresh SQL query first
const rows = await fetchPost('/api/query/sql', { stmt: "SELECT id FROM blocks WHERE content='TODO' AND type='l' LIMIT 1" });
await fetchPost('/api/block/updateTaskListItemMarker', { id: rows[0].id, marker: 'x' }); Defensive patterns
Strategy: validation
Validate before calling
const rows = await fetchPost('/api/query/sql', { stmt: `SELECT id FROM blocks WHERE id='${id}'` });
if (!rows.length) throw new Error(`block ${id} no longer exists; re-resolve its ID`); Type guard
const blockExists = (rows) => Array.isArray(rows) && rows.length > 0 && typeof rows[0].id === 'string';
Try / catch
try {
await fetchPost('/api/block/updateTaskListItemMarker', { id, marker });
} catch (e) {
if (e?.msg === 'block not found') {
const fresh = await sql("SELECT id FROM blocks WHERE type='l' AND subtype='t' LIMIT 1");
if (fresh.length) return retryWith(fresh[0].id);
}
throw e;
} Prevention
- Never cache block IDs across sessions or syncs; re-query before writes
- After sync or bulk edits, wait for indexing to finish before mutating blocks
- Validate IDs against the blocks table in batch scripts and skip missing ones
When it happens
Trigger: Calling POST /api/block/updateTaskListItemMarker (or batchUpdateTaskListItemMarker) with an ID that exists in the block index (blocks table) but not in the parsed tree: a stale/deleted block ID, an ID whose document was just deleted or renamed, or an index that has not caught up after a sync.
Common situations: Scripts or plugins caching block IDs that were deleted; a sync operation replaced the document; the blocks SQLite index is stale relative to the .sy files on disk; passing an ID from another notebook or an old history snapshot.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- block is not a task list item
- block not found or its encrypted notebook is locked
- task list item marker not found
- Content block with id
- 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/07f4fc6f49446008.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/api/block_op.go:51
func buildUpdatedTaskListItemBlockDOM(id, marker string, luteEngine *lute.Lute) (data string, err error) {
block, err := model.GetBlock(id, nil)
if err != nil {
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")View on GitHub (pinned to 9f775e8a12)