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

  1. Confirm the block ID still exists via /api/block/getBlockInfo before performing the operation.
  2. Treat this as an already-deleted success case: catch the error and skip re-deleting.
  3. Re-read the block IDs from the document after any sync/delete activity.
  4. 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

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


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)