siyuan-note/siyuan · error

load block tree [ ] failed

Error message

load block tree [%s] failed: %w

What it means

When buildBlockUpdateOperations cannot load the existing tree for an input ID (cache miss and loadTree returns an error), it wraps the failure as "load block tree [%s] failed: %w" with the ID and underlying cause. This means the block's document could not be read from disk/index before applying the update.

Solutions

  1. Check that the notebook containing the block still exists and the .sy file is present under data/<box>/.siyuan? read via /api/filetree/getHPathByID.
  2. Rebuild the index (设置 - 关于 - rebuild / /api/filetree/refreshFiletree or full reindex) if the doc was just synced.
  3. Inspect the wrapped cause (%w) in server logs to distinguish ENOENT from permission errors.
  4. Restore the missing document from sync/history before retrying the update.

Example fix

// before
await updateBlock(id, newData); // fails if doc file is gone
// after
const info = await fetchPost('/api/filetree/getHPathByID', { id });
if (info.code !== 0) { await restoreFromHistoryOrSync(id); }
await updateBlock(id, newData);
Defensive patterns

Strategy: fallback

Validate before calling

async function treeLoadable(id) {
  const res = await fetchPost('/api/filetree/getHPathByID', { id });
  return res.code === 0;
}

Try / catch

try { await updateBlocks(inputs); } catch (e) { if (String(e.msg).includes('load block tree')) { await restoreDocFromHistoryOrSync(id); await updateBlocks(inputs); } else throw e; }

Prevention

When it happens

Trigger: Updating a block whose document file is missing or unreadable — notebook not fully indexed, .sy file deleted by sync, disk/permission error, or the ID belongs to a closed/removed notebook.

Common situations: Sync races deleting the document between the UI read and the update; workspace data directory moved or partially copied; failing disk or read-only filesystem; notebook removed while a plugin still references its blocks.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/3bcf78442e3f79f8. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/block_update.go:113

		data, dataTree, parseErr := parseBlockUpdateData(input.Data, input.DataType, luteEngine)
		if parseErr != nil {
			return nil, nil, parseErr
		}

		var oldTree *parse.Tree
		var cacheKey blockUpdateTreeKey
		hasCacheKey := false
		if blockTree := resolveTree(input.ID); nil != blockTree {
			cacheKey = blockUpdateTreeKey{boxID: blockTree.BoxID, rootID: blockTree.RootID}
			hasCacheKey = true
			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)
		}

View on GitHub (pinned to 9f775e8a12)