siyuan-note/siyuan · error
invalid notebook ID
Error message
invalid notebook ID
What it means
First guard in RemoveBox: if !ast.IsNodeIDPattern(boxID) the function returns a raw errors.New('invalid notebook ID') (not localised). ast.IsNodeIDPattern validates that the ID matches SiYuan's node-ID format (the format generated by ast.NewNodeID). This rejects arbitrary/garbage IDs before any filesystem or database work.
Source
Thrown at kernel/model/mount.go:188
}
return
}
func collectBoxDeletedAttributeViewBlocks(boxID string) (ret map[string]map[string]struct{}, err error) {
rootIDs := treenode.GetRootBlockIDsByBoxID(boxID)
if 1 > len(rootIDs) {
return map[string]map[string]struct{}{}, nil
}
boundAVIDs, err := sql.QueryBoundBlockAVIDsInBox(nil, rootIDs, boxID)
if nil != err {
return nil, err
}
return groupDeletedAttributeViewBlocks(boundAVIDs), nil
}
func RemoveBox(boxID string) (err error) {
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)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Always pass the notebook ID returned by CreateBox/ListNotebooks, not its display name.
- Validate with ast.IsNodeIDPattern(boxID) before calling RemoveBox and reject early.
- If exposing removal via an API, normalise and validate the ID at the trust boundary.
Example fix
// before
err := model.RemoveBox("My Notebook") // 'invalid notebook ID'
// after
if !ast.IsNodeIDPattern(boxID) {
return fmt.Errorf("invalid notebook ID: %q", boxID)
}
err := model.RemoveBox(boxID) Defensive patterns
Strategy: validation
Validate before calling
if !ast.IsNodeIDPattern(boxID) {
return fmt.Errorf("invalid notebook ID: %q", boxID)
} Type guard
// isNotebookID narrows a string to a valid SiYuan notebook ID.
func isNotebookID(id string) bool { return ast.IsNodeIDPattern(id) } Try / catch
null
Prevention
- Always use IDs from CreateBox/ListNotebooks, never display names.
- Validate IDs at API trust boundaries with ast.IsNodeIDPattern.
When it happens
Trigger: Calling RemoveBox with a boxID that is not a valid SiYuan node ID: empty string, a human name, a path, a UUID, a truncated/altered ID, or any string failing the node-ID regex. The check runs before the boxLock and reserved-filename checks.
Common situations: Programmatic caller passing a UI label instead of an ID; ID read from an untrusted source; copy-paste truncation; integration tests using dummy IDs like 'test' or '1'; confusion between notebook name and notebook ID.
Related errors
- can not remove [%s] caused by it is a reserved file
- 106
- 0
- 239
- can not remove [%s] caused by it is not a dir
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/f476d6e602718b63.
Report an issue: GitHub.