siyuan-note/siyuan · error
invalid notebook ID
Error message
invalid notebook ID
What it means
RemoveBox validates that boxID matches ast.IsNodeIDPattern (a SiYuan node-ID format: 14-digit timestamp + 6 random chars, e.g. 20240102150405-abcdefg) before doing any work. If the ID is not a valid node ID it returns the literal error "invalid notebook ID". This guards the notebook-removal API against malformed or arbitrary path-like identifiers.
Solutions
- Fetch the correct notebook ID first via /api/notebook/lsNotebooks and pass the id field exactly (20 chars, format tttttttttttttt-xxxxxxx)
- Check the value for stray whitespace, quotes, or path separators; trim or re-read it from the notebooks list response
- If the ID comes from stored config, verify the notebook still exists and regenerate the reference from lsNotebooks
Example fix
// before: using notebook name as ID
fetchPost("/api/notebook/removeNotebook", {notebook: "My Notes"})
// after: resolve ID first
const notebooks = await fetchPost("/api/notebook/lsNotebooks", {})
const box = notebooks.notebooks.find(nb => nb.name === "My Notes")
await fetchPost("/api/notebook/removeNotebook", {notebook: box.id}) Defensive patterns
Strategy: validation
Validate before calling
var nodeIDRe = regexp.MustCompile(`^\d{14}-[0-9a-z]{7}$`)
func isValidNotebookID(id string) bool { return nodeIDRe.MatchString(id) }
// call removeNotebook only if isValidNotebookID(notebookID) Try / catch
err := removeNotebook(boxID)
if err != nil && err.Error() == "invalid notebook ID" {
// re-resolve the ID via lsNotebooks and retry once
} Prevention
- Always obtain notebook IDs from /api/notebook/lsNotebooks, never from names or paths
- Validate the 14-7 pattern (id-time-rand) before sending removal requests
- Trim whitespace from IDs read from logs or config before passing them
When it happens
Trigger: Calling the kernel API /api/notebook/removeNotebook (or RemoveBox directly) with an ID that is not a 20-character node ID — e.g. a notebook name, a relative path, an empty string, an old-style ID, or a value copied from a different field.
Common situations: Scripting against the HTTP API with the notebook name instead of its ID; stale client code holding IDs from a pre-rename dataset; plugin code concatenating path components into the ID; trimmed/truncated IDs from logs or config files.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- can not remove [ ] caused by it is a reserved file
- Field [id] must not be empty
- invalid AI editor action ID
- invalid box document ID
- invalid box ID [ ]
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/f476d6e602718b63.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/mount.go:269
}
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 9f775e8a12)