siyuan-note/siyuan · error

query database-bound blocks in notebook

Error message

query database-bound blocks in notebook [%s] failed: %w

What it means

Before deleting the notebook directory, RemoveBox calls collectBoxDeletedAttributeViewBlocks, which queries the SQLite index for database (attribute-view) blocks bound to the notebook so bound rows can be cleaned up after deletion. If that query fails, removal aborts with "query database-bound blocks in notebook [%s] failed: %w" wrapping the underlying SQL error. The notebook is left intact because the query must succeed before the directory is removed.

Solutions

  1. Inspect the wrapped (%w) inner error in logs to identify the SQL failure; fix that root cause first
  2. Ensure no second kernel instance is using the same workspace (siyuan.db file lock)
  3. If the database is corrupted, close the kernel and run SQLite integrity/recovery (PRAGMA integrity_check; .recover) on data/index/siyuan.db, then rebuild the index (Ctrl+F5 / 'Rebuild Index')
  4. After the DB is healthy, retry the notebook removal — the pre-delete collection is re-run on each attempt
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check DB readability before attempting removal
rows, err := query("SELECT count(*) FROM blocks WHERE box = ?", boxID)
if err != nil { return fmt.Errorf("index db unreadable for %s: %w", boxID, err) }

Try / catch

if err := removeNotebook(boxID); err != nil {
    var qe *fmt.WrapError // unwrap the %w chain
    if strings.HasPrefix(err.Error(), "query database-bound blocks") {
        // do NOT retry blindly: repair/rebuild siyuan.db first, then retry
        rebuildIndex()
        return removeNotebook(boxID)
    }
    return err
}

Prevention

When it happens

Trigger: sql.QueryBoundBlockAVIDsInBox failing while collecting custom-av bindings — usually when siyuan.db is corrupted, locked by another writer, or the index queue is in a bad state; it can also surface after a crashed index transaction left the blocks tables unreadable for that box.

Common situations: SQLite database corruption after a hard power-off or disk-full during indexing; database file locked by a second SiYuan instance pointed at the same workspace; version-upgrade migrations partially applied; antivirus locking siyuan.db on Windows.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/mount.go:286

	if !ast.IsNodeIDPattern(boxID) {
		return errors.New("invalid notebook ID")
	}
	if _, loaded := boxLock.LoadOrStore(boxID, true); loaded {
		err = errors.New(Conf.language(239))
		return
	}
	defer boxLock.Delete(boxID)

	if util.IsReservedFilename(boxID) {
		return fmt.Errorf("can not remove [%s] caused by it is a reserved file", boxID)
	}

	FlushTxQueue()
	sql.FlushQueue()
	// 索引和笔记本目录删除后无法再读取 custom-avs,需提前收集;实际删除成功后再清理绑定行。
	deletedAttrViewBlockIDs, err := collectBoxDeletedAttributeViewBlocks(boxID)
	if nil != err {
		return fmt.Errorf("query database-bound blocks in notebook [%s] failed: %w", boxID, err)
	}
	isUserGuide := IsUserGuide(boxID)
	localPath := filepath.Join(util.DataDir, boxID)
	if !filelock.IsExist(localPath) {
		removeHPathRefreshBox(boxID)
		forgetRuntimeNormalBox(boxID)
		removeMasterPasswordMigrationBox(boxID)
		return
	}
	if !gulu.File.IsDir(localPath) {
		return fmt.Errorf("can not remove [%s] caused by it is not a dir", boxID)
	}

	// 删目录前固定加密状态,确保后续历史、资源和索引清理始终使用同一个安全边界。
	isEncrypted := IsEncryptedBox(boxID)
	if !isUserGuide {
		if err = EnsureAssetPrefixLocal(localPath); err != nil {
			return

View on GitHub (pinned to 9f775e8a12)