siyuan-note/siyuan · error

Content block with id

Error message

Content block with id [%s] not found

What it means

Conf.Language(15) renders as 'Content block with id [%s] not found'. GetHeadingDeleteTransaction loads the document tree via LoadTreeByBlockID(id) — which succeeded — but treenode.GetNodeInTree(tree, id) then fails to find the node with that ID inside the loaded tree. This means the blocktree index still maps the ID to a document, but the document content no longer contains it: a stale index or the block was deleted/renumbered since indexing. Returned by POST /api/block/getHeadingDeleteTransaction with code=-1 and closeTimeout=7000.

Solutions

  1. Re-fetch the current block ID (e.g. via /api/query/SQL or /api/block/getBlockInfo) and retry with a fresh ID
  2. If IDs are persistently missing, rebuild the index (Settings - Search - Rebuild index, or the doc tree rebuild option)
  3. Flush pending transactions before relying on IDs right after heavy edits
  4. Handle the error gracefully in the UI (this is what closeTimeout=7000 in the response hints at)

Example fix

// before
const tx = await fetchSyncPost("/api/block/getHeadingDeleteTransaction", {id: cachedHeadingID})

// after: refresh the ID before requesting the delete transaction
const rows = await fetchSyncPost("/api/query/sql", {stmt: `select id from blocks where id = '${cachedHeadingID}'`})
if (rows.data.length === 0) {
    // block gone: drop the stale reference
    return
}
const tx = await fetchSyncPost("/api/block/getHeadingDeleteTransaction", {id: cachedHeadingID})
Defensive patterns

Strategy: validation

Validate before calling

// Verify the block still exists before building a heading-delete transaction
rows := sqlQuery(fmt.Sprintf("select id from blocks where id = '%s'", id))
if len(rows) == 0 { return fmt.Errorf("block %s no longer exists", id) }
transaction, err := model.GetHeadingDeleteTransaction(id)

Type guard

func blockExists(id string) bool {
    rows, err := model.QuerySQL(fmt.Sprintf("select id from blocks where id = '%s'", id))
    return nil == err && 0 < len(rows)
}

Try / catch

transaction, err := model.GetHeadingDeleteTransaction(id)
if err != nil {
    if strings.Contains(err.Error(), "not found") {
        // stale ID: drop cached references and refresh the doc; do not blindly retry the same ID
        return refreshAndRetryOnce(id)
    }
    return err
}

Prevention

When it happens

Trigger: POST /api/block/getHeadingDeleteTransaction with a heading ID that was deleted in the meantime (edited in another window/device, synced change, or refactored by a plugin) while the blocktree index was not yet updated; also an ID that only exists in a different document than the tree it resolved to.

Common situations: Stale block IDs cached by frontend code or plugins; concurrent edits via sync or multiple windows; index not rebuilt after data rollback or manual workspace surgery; passing a document ID where a heading ID is required in rare resolution edge cases.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/9b5fcfe33b85f7ce. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/block.go:843

	if !sameTree {
		if err = indexWriteTreeUpsertQueue(defTree); err != nil {
			return
		}
	}
	FlushTxQueue()
	util.ReloadUI()
	return
}

func GetHeadingDeleteTransaction(id string) (transaction *Transaction, err error) {
	tree, err := LoadTreeByBlockID(id)
	if err != nil {
		return
	}

	node := treenode.GetNodeInTree(tree, id)
	if nil == node {
		err = fmt.Errorf(Conf.Language(15), id)
		return
	}

	if ast.NodeHeading != node.Type {
		return
	}

	var nodes []*ast.Node
	nodes = append(nodes, node)
	nodes = append(nodes, treenode.HeadingChildren(node)...)
	hiddenNodes := map[string]bool{}
	for _, hidden := range treenode.CollectFoldHiddenNodes(node.Parent) {
		hiddenNodes[hidden.ID] = true
	}

	transaction = &Transaction{}
	luteEngine := util.NewLute()
	for _, n := range nodes {

View on GitHub (pinned to afa823b6b4)