siyuan-note/siyuan · error
block not found [ ]
Error message
block not found [%s]
What it means
PerformBlockOperation loads the tree for operation.ID and then looks the node up; if the tree loads but no node with that ID exists, it returns this error. It guards the delete-block operation against acting on a nonexistent block and happens before any transaction is created.
Solutions
- Confirm the block ID still exists via /api/block/getBlockInfo before performing the operation.
- Treat this as an already-deleted success case: catch the error and skip re-deleting.
- Re-read the block IDs from the document after any sync/delete activity.
- Do not route document-level deletions through the block operation API; use /api/filetree/removeDoc.
Example fix
// before
await fetchPost('/api/block/performBlockOperation', { operations: [{ action: 'delete', id: id }] });
// after
const info = await fetchPost('/api/block/getBlockInfo', { id });
if (info.code === 0) {
await fetchPost('/api/block/performBlockOperation', { operations: [{ action: 'delete', id }] });
} // else: already deleted, nothing to do Defensive patterns
Strategy: try-catch
Validate before calling
async function canDeleteBlock(id) {
const res = await fetchPost('/api/block/getBlockInfo', { id });
return res.code === 0 && res.data.type !== 'd';
} Try / catch
try { await performBlockOperation('delete', id); } catch (e) { if (String(e.msg).includes('block not found')) return; /* already gone */ throw e; } Prevention
- Verify block existence before delete calls.
- Treat 'not found' on delete as success (idempotent deletes).
- Keep block ID references short-lived.
When it happens
Trigger: PerformBlockOperation with Action="delete" (and a valid ID format) where LoadTreeByBlockID succeeds but the node is absent from the tree — e.g. an ID that equals the root ID handled elsewhere, a removed block, or an ID pointing into a different tree.
Common situations: API clients retrying a delete that already succeeded; synced deletions landing between the caller's read and the call; automation passing document root IDs through the generic block-op endpoint.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- block [ ] not found
- Conf.Language(15) formatted with block id (localized…
- document cannot be deleted as a block
- block not found
- block not found or its encrypted notebook is locked
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/d23d8f782ad32167.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/block_operation.go:46
flushTx(queued)
}
switch operation.Action {
case "appendInsert", "insert":
if operation.Action == "appendInsert" || operation.PreviousID == "" && operation.NextID == "" {
if err = treenode.CheckContainerParent(operation.ParentID); err != nil {
return nil, err
}
}
case "delete":
// 外部删除请求必须命中现存节点,编辑器内部仍可使用幂等删除。
tree, loadErr := LoadTreeByBlockID(operation.ID)
if loadErr != nil {
return nil, loadErr
}
node := treenode.GetNodeInTree(tree, operation.ID)
if node == nil {
return nil, fmt.Errorf("block not found [%s]", operation.ID)
}
if node == tree.Root {
return nil, fmt.Errorf("document cannot be deleted as a block [%s]", operation.ID)
}
default:
return nil, fmt.Errorf("unsupported block operation [%s]", operation.Action)
}
tx := &Transaction{DoOperations: []*Operation{operation}}
if err = performTxSyncLocked(tx); err != nil {
return nil, err
}
return []*Transaction{tx}, nil
}
View on GitHub (pinned to 9f775e8a12)