siyuan-note/siyuan · critical

block rowid not found after insert

Error message

block rowid not found after insert: %s

What it means

After bulk-inserting blocks into the blocks/FTS tables, insertBlocks0 re-queries rowids for each inserted block ID to build FTS rows. If a freshly inserted ID is missing from the returned rowid map, the kernel treats it as a broken invariant and aborts the FTS population to avoid misaligned FTS arguments. The code comments this as 'theoretically impossible' defensive handling.

Solutions

  1. Retry the insert operation; the error aborts before FTS misalignment so re-running usually succeeds
  2. Check for concurrent writers to the same database (only one kernel should own the workspace)
  3. Verify database integrity (sqlite3 'PRAGMA integrity_check') and rebuild the index
  4. Inspect the reported block ID for duplicates or anomalies in the source document
Defensive patterns

Strategy: retry

Try / catch

err := insertBlocks(tx, blocks)
if err != nil && strings.Contains(err.Error(), "block rowid not found after insert") {
    return retryInsert(tx, blocks) // abort happened before FTS misalignment; safe to retry
}

Prevention

When it happens

Trigger: insertBlocks bulk-insert completes but the subsequent rowid lookup fails to return a row for a block ID — e.g. due to concurrent writes, trigger/replica anomalies, ID collisions, or a partial insert failure not surfaced earlier.

Common situations: Concurrent document editing racing with bulk insert; database corruption or unusual sync states; custom tooling writing directly to siyuan.db and deleting rows mid-flight.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at kernel/sql/upsert.go:143

	hashBuf.WriteString("blocks")
	evtHash := fmt.Sprintf("%x", sha256.Sum256(hashBuf.Bytes()))[:7]
	// 使用下面的 EvtSQLInsertBlocksFTS 就可以了
	//eventbus.Publish(eventbus.EvtSQLInsertBlocks, context, current, total, len(bulk), evtHash)

	// external content 模式下,FTS 行必须使用 blocks 的 rowid。刚插入的 blocks 行在同一事务内可见,
	// 这里按 id 反查 rowid,再用与 bulk 顺序对齐的 rowid 拼装 FTS 写入参数。
	blockRowIDs, err := queryBlockRowIDsTx(tx, bulk)
	if err != nil {
		return
	}
	ftsValueStrings := make([]string, 0, len(bulk))
	ftsValueArgs := make([]any, 0, len(bulk)*strings.Count(BlocksFTSPlaceholder, "?"))
	for _, b := range bulk {
		rowID, ok := blockRowIDs[b.ID]
		if !ok {
			// 理论上不会发生(刚插入即反查),防御性处理避免错位
			logging.LogErrorf("query block rowid failed after insert: id=%s not found", b.ID)
			err = fmt.Errorf("block rowid not found after insert: %s", b.ID)
			return
		}
		ftsValueStrings = append(ftsValueStrings, BlocksFTSPlaceholder)
		ftsValueArgs = append(ftsValueArgs, rowID)
		ftsValueArgs = append(ftsValueArgs, any(b.ID))
		ftsValueArgs = append(ftsValueArgs, any(b.ParentID))
		ftsValueArgs = append(ftsValueArgs, any(b.RootID))
		ftsValueArgs = append(ftsValueArgs, any(b.Hash))
		ftsValueArgs = append(ftsValueArgs, any(b.Box))
		ftsValueArgs = append(ftsValueArgs, any(b.Path))
		ftsValueArgs = append(ftsValueArgs, any(b.HPath))
		ftsValueArgs = append(ftsValueArgs, any(b.Name))
		ftsValueArgs = append(ftsValueArgs, any(b.Alias))
		ftsValueArgs = append(ftsValueArgs, any(b.Memo))
		ftsValueArgs = append(ftsValueArgs, any(b.Tag))
		ftsValueArgs = append(ftsValueArgs, any(b.Content))
		ftsValueArgs = append(ftsValueArgs, any(b.FContent))
		ftsValueArgs = append(ftsValueArgs, any(b.Markdown))

View on GitHub (pinned to 9f775e8a12)