siyuan-note/siyuan · error

block not found

Error message

block not found

What it means

Sentinel error model.ErrBlockNotFound (kernel/model/tree.go:204), the most common not-found error in the kernel. It means a block ID has no entry in the blocktree (treenode.GetBlockTree returned nil) or a document path failed validation. Dozens of call sites return it (block.go, heading.go, listitem.go, search.go, export.go), and the transaction layer maps it to TxErrCodeBlockNotFound.

Solutions

  1. Verify the id exists first, e.g. POST /api/query/block {"id": "<blockID>"} or /api/block/getBlockInfo
  2. If indexing may be in flight, wait for it to finish and retry once
  3. If the id is persistently missing, rebuild the index (设置 - 搜索 - 重建索引) or verify the block was not deleted by the user
  4. Match with errors.Is(err, model.ErrBlockNotFound); in transactions treat TxErr code TxErrCodeBlockNotFound as 'refresh your id cache'
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the block exists before operating on it:
// POST /api/query/block {"id": "<blockID>"}
// Empty result -> do not call block-mutating APIs; refresh your id source.

Type guard

func isBlockNotFound(err error) bool {
	return errors.Is(err, model.ErrBlockNotFound)
}

Try / catch

if err := model.SomeBlockOp(id); err != nil {
	switch {
	case errors.Is(err, model.ErrIndexing):
		// wait for indexing, retry once
	case errors.Is(err, model.ErrBlockNotFound):
		// id is stale or deleted - drop it from caches, do not retry
	default:
		// propagate
	}
}

Prevention

When it happens

Trigger: Operations on a stale or deleted block ID (user removed the block after your code captured the id); calling block APIs while the database is still indexing; invalid document paths in file ops (tests in file_test.go show malformed parent paths also map to this sentinel).

Common situations: Plugins caching block IDs in long-lived state; API retries racing user deletions; fresh kernel boot before indexing commits; referencing blocks of an unindexed notebook.

Related errors


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

Appendix: source

Thrown at kernel/model/tree.go:204

		relPath := filepath.ToSlash(strings.TrimPrefix(localPath, filepath.Join(util.DataDir, boxID)+string(os.PathSeparator)))
		if data, err = DecryptFile(boxID, relPath, dek, data); err != nil {
			logging.LogErrorf("decrypt tree [path=%s] failed: %s", localPath, err)
			return
		}
	}

	ret, err = dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
	if err != nil {
		logging.LogErrorf("parse json to tree [%s] failed: %s", localPath, err)
		return
	}
	return
}

var (
	ErrBoxNotFound   = errors.New("notebook not found")
	ErrBoxClosed     = errors.New("notebook closed")
	ErrBlockNotFound = errors.New("block not found")
	ErrTreeNotFound  = errors.New("tree not found")
	ErrIndexing      = errors.New("indexing")
	ErrBoxUnindexed  = errors.New("notebook unindexed")
	ErrInvalidID     = errors.New("invalid id")
)

func LoadTreeByBlockIDWithReindex(id string) (ret *parse.Tree, err error) {
	return LoadTreeByBlockIDWithReindexInBox(id, "")
}

// LoadTreeByBlockIDWithReindexInBox 与 LoadTreeByBlockIDWithReindex 一致,但按 boxID 路由 blocktree 查询。
func LoadTreeByBlockIDWithReindexInBox(id, boxID string) (ret *parse.Tree, err error) {
	if "" == id {
		logging.LogWarnf("block id is empty")
		return nil, ErrTreeNotFound
	}

	bt := treenode.GetBlockTreeInBox(id, boxID)

View on GitHub (pinned to afa823b6b4)