siyuan-note/siyuan · critical

block tree database is unavailable

Error message

block tree database is unavailable

What it means

errBlockTreeDBUnavailable, declared in kernel/treenode/blocktree.go. It is returned by scanQueryRow when the *sql.Row it received is nil, which happens when the package-level `db` handle is nil — i.e. initDatabase / initDBConnection has not run (or the kernel is shutting down). Callers like GetBlockTreeInExactBox treat it silently (not logged) because it is an init-order condition, not data corruption.

Source

Thrown at kernel/treenode/blocktree.go:52

	"github.com/88250/lute/parse"
	"github.com/siyuan-note/logging"
	"github.com/siyuan-note/siyuan/kernel/util"
)

type BlockTree struct {
	ID       string // 块 ID
	RootID   string // 根 ID
	ParentID string // 父 ID
	BoxID    string // 笔记本 ID
	Path     string // 文档数据路径
	HPath    string // 文档可读路径
	Updated  string // 更新时间
	Type     string // 类型
}

var (
	db                        *sql.DB
	errBlockTreeDBUnavailable = errors.New("block tree database is unavailable")

	initDatabaseLock = sync.RWMutex{}
)

func initDatabase(forceRebuild bool) {
	initDatabaseLock.Lock()
	defer initDatabaseLock.Unlock()

	initDBConnection()

	if !forceRebuild {
		if !gulu.File.IsExist(util.BlockTreeDBPath) {
			forceRebuild = true
		}
	}
	if !forceRebuild {
		// 校验块树表是否可用,避免因上次重建被中断导致数据库文件存在但表缺失
		var table string

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Ensure the kernel boot sequence has completed treenode.InitBlockTree / initDatabase before issuing block queries.
  2. If init failed, inspect the kernel log for the preceding LogFatalf/LogErrorf about blocktree DB creation — the DB file may be locked, on a read-only volume, or corrupt.
  3. Treat errBlockTreeDBUnavailable as a fatal init-order signal: surface it to the user rather than retrying blindly.

Example fix

// before: blocktree read issued before boot finished
bt := treenode.GetBlockTree(id) // returns nil, scan logs nothing

// after: wait for init, then query
if err := treenode.InitBlockTree(false); err != nil {
    return fmt.Errorf("blocktree init failed: %w", err)
}
bt := treenode.GetBlockTree(id)
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the blocktree db handle is live before querying.
func ensureBlockTreeReady() error {
    // Re-initialise if missing; initDatabase is idempotent under initDatabaseLock.
    treenode.InitBlockTree(false) // or whatever the boot entry exposes
    return nil
}

if err := ensureBlockTreeReady(); err != nil { return err }
bt := treenode.GetBlockTree(id)

Try / catch

bt := treenode.GetBlockTree(id)
if bt == nil {
    // GetBlockTree swallows errBlockTreeDBUnavailable; treat nil during boot as fatal
    if !booted { return fmt.Errorf("blocktree db not yet initialised") }
}

Prevention

When it happens

Trigger: Any blocktree read that goes through scanQueryRow (e.g. GetBlockTree, GetBlockTreeInExactBox) before initDatabase has established the global `db` handle, or after it has been torn down. The kernel's Boot sequence must call the treenode init before serving requests.

Common situations: Calling blocktree lookups during very early startup before the blocktree DB is opened; invoking operations after a fatal DB init failure (LogFatalf with ExitCodeUnavailableDatabase); races during shutdown.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/fb48dc705a1ab597. Report an issue: GitHub.