siyuan-note/siyuan · error

block [%s] not found

Error message

block [%s] not found

What it means

Returned by buildBlockUpdateOperations when loadTree succeeded with a non-empty Root, but treenode.GetNodeInTree(oldTree, input.ID) returned nil. The tree is fine; the specific block ID is not a node in it - same desync class as the block.go not-found errors, but inside the update pipeline.

Source

Thrown at kernel/model/block_update.go:125

			oldTree = treeCache[cacheKey]
		}
		if nil == oldTree {
			var loadErr error
			oldTree, loadErr = loadTree(input.ID)
			if loadErr != nil {
				return nil, nil, fmt.Errorf("load block tree [%s] failed: %w", input.ID, loadErr)
			}
			if nil == oldTree || nil == oldTree.Root {
				return nil, nil, fmt.Errorf("load block tree [%s] failed: tree is empty", input.ID)
			}
			treeCache[blockUpdateTreeKey{boxID: oldTree.Box, rootID: oldTree.ID}] = oldTree
			if hasCacheKey {
				treeCache[cacheKey] = oldTree
			}
		}
		oldNode := treenode.GetNodeInTree(oldTree, input.ID)
		if nil == oldNode {
			return nil, nil, fmt.Errorf("block [%s] not found", input.ID)
		}

		if _, ok := rootIDSet[oldTree.ID]; !ok {
			rootIDSet[oldTree.ID] = struct{}{}
			rootIDs = append(rootIDs, oldTree.ID)
		}

		if ast.NodeDocument == oldNode.Type {
			if validateErr := treenode.ValidateBlockSubtree(dataTree.Root); validateErr != nil {
				return nil, nil, validateErr
			}
			for n := oldTree.Root.FirstChild; nil != n; n = n.Next {
				if !n.IsBlock() || ast.NodeKramdownBlockIAL == n.Type {
					continue
				}
				operations = append(operations, &Operation{Action: "delete", ID: n.ID, Data: map[string]any{
					"createEmptyParagraph": false, // 清空文档后前端不要创建空段落
				}})

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Rebuild the index to reconcile blocktree.db with on-disk trees.
  2. Verify via SQL that the block ID still maps to the expected root ID.
  3. Reload the document and re-issue the update with a current block ID.
  4. Retry once if a concurrent transaction is plausible; if it persists, surface 'block gone' to the user.
Defensive patterns

Strategy: validation

Validate before calling

row, _ := sql.QueryRow("SELECT root_id FROM blocks WHERE id = ?", input.ID)
// confirm input.ID is still mapped to a node in its tree before update

Try / catch

ops, roots, err := buildBlockUpdateOperations(inputs, resolver, loader)
if err != nil && strings.Contains(err.Error(), "not found") {
    // reload document, refresh block IDs, retry
}

Prevention

When it happens

Trigger: Block was moved/deleted between resolveTree's view and the load; blocktree entry points at a tree that no longer hosts the block; concurrent transaction rewrote the tree.

Common situations: Sync conflict; another client edited the document; blocktree lag after a recent move operation.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/ecf09791c3c95326. Report an issue: GitHub.