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
- Check that the notebook containing the block still exists and the .sy file is present under data/<box>/.siyuan? read via /api/filetree/getHPathByID.
- Rebuild the index (设置 - 关于 - rebuild / /api/filetree/refreshFiletree or full reindex) if the doc was just synced.
- Inspect the wrapped cause (%w) in server logs to distinguish ENOENT from permission errors.
- 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
- Confirm notebooks are fully indexed/synced before bulk updates.
- Watch the wrapped cause in server logs to distinguish missing files from permission errors.
- Avoid updating blocks in notebooks that are mid-sync or mid-reindex.
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
- Conf.Language(77)
- Conf.Language(77), msg: get endpoint
- dir [ ] has more than 1 file:\n
- load tree failed
- workspace dir [ ] is in third party sync dir
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)