siyuan-note/siyuan · error
query database-bound blocks in notebook [%s] failed: %w
Error message
query database-bound blocks in notebook [%s] failed: %w
What it means
Thrown by RemoveBox in kernel/model/mount.go:205 when collecting attribute-view-bound blocks before deleting a notebook. It wraps an underlying error from collectBoxDeletedAttributeViewBlocks -> sql.QueryBoundBlockAVIDsInBox, so the SQLite query that finds which database (attribute view) blocks live in the notebook failed. The notebook is not removed and the transaction is aborted.
Source
Thrown at kernel/model/mount.go:205
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) {
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 isEncrypted {
// 加密索引先持有生命周期租约再获取索引锁,因此删除也必须先结束生命周期,保持锁顺序一致。
unmount0(boxID)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Retry the remove after the kernel reports it is idle (no indexing/reindex in progress); the boxLock and queue flush serialize correctly on a clean kernel.
- Check kernel logs for the wrapped error (%w) — it identifies the exact SQL failure (busy, locked, no such table) and points to the next step.
- If the wrapped error is 'database is locked', ensure no second SiYuan process or sync job holds siyuan.db; close other instances and retry.
- If the DB schema is corrupt, restore from history/repo or run the kernel's index rebuild before retrying the remove.
Example fix
// before
err := model.RemoveBox(boxID)
// after — surface the wrapped cause to the user instead of swallowing it
if err := model.RemoveBox(boxID); err != nil {
logging.LogErrorf("remove notebook %s failed: %s", boxID, err)
return err // includes the wrapped SQL cause via %%w
} Defensive patterns
Strategy: try-catch
Validate before calling
// Avoid calling RemoveBox while indexing is active.
if model.IsIndexing() {
return errors.New("wait for indexing to finish before removing a notebook")
}
err := model.RemoveBox(boxID) Type guard
// boxID must be a 22-char base32-style ID accepted by ast.IsNodeIDPattern.
func validBoxID(id string) bool { return ast.IsNodeIDPattern(id) } Try / catch
err := model.RemoveBox(boxID)
if err != nil {
if strings.Contains(err.Error(), "query database-bound blocks") {
// transient — retry once after queues drain, then report the wrapped cause
time.Sleep(2 * time.Second)
err = model.RemoveBox(boxID)
}
return err
} Prevention
- Do not run RemoveBox concurrently with sync or reindex — both touch siyuan.db.
- Always log the wrapped %%w cause; the surface message alone hides whether it is contention or corruption.
- Keep one kernel instance per workspace to avoid cross-process SQLite locks.
When it happens
Trigger: Calling /api/notebook/removeNotebook (model.RemoveBox) while the SQLite database (siyuan.db) is locked, the schema is mid-migration, the index queue is corrupt, or the kernel is being shut down. FlushTxQueue and sql.FlushQueue run first, so any failure inside the subsequent SQL read against siyuan.db surfaces here.
Common situations: Concurrent notebook operations hitting SQLite contention, a damaged siyuan.db after a crash, antivirus/file-locking on Windows holding the DB file, or running remove while a reindex is still draining the sql queue.
Related errors
- invalid box id
- Remove notebook [%s] path [%s] failed: %s
- invalid notebook ID
- 239
- can not remove [%s] caused by it is a reserved file
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/c4d6c8d9bb643c1c.
Report an issue: GitHub.