siyuan-note/siyuan · error
notebook [ ] not found
Error message
notebook [%s] not found
What it means
Notebook sort planning resolves each item's ID against the map of opened (and unlocked) notebooks. If the ID is not a currently opened notebook, it fails with "notebook [%s] not found". The kernel only sorts notebooks that are open in this session; closed or nonexistent notebooks are rejected.
Solutions
- Open the referenced notebook (listNotebooks / open API) before sorting, or drop it from the sort list
- Refresh the notebook list and rebuild the sort payload from currently open notebooks only
- Verify the ID is a notebook ID (matches a directory under data/) not a document block ID
Example fix
// before: sort stale cached list
customSortNotebooks(cachedNotebookIDs)
// after: intersect with currently open notebooks
open := map[string]bool{}
for _, b := range listNotebooks() { open[b.ID] = true }
valid := filter(cachedNotebookIDs, func(id string) bool { return open[id] })
customSortNotebooks(valid) Defensive patterns
Strategy: validation
Validate before calling
const openIds = (await api.listNotebooks()).data.notebooks.map(n => n.id); const valid = notebookSorts.filter(i => openIds.includes(i.id));
Type guard
const isOpenNotebook = (id, openSet) => openSet.has(id);
Try / catch
try { await api.sortNotebooks(notebookSorts); } catch (e) { if (String(e).includes('notebook [') && String(e).includes('] not found')) { await refreshOpenNotebooks(); retry(); } else { throw e; } } Prevention
- Build sort payloads from the currently open notebook list, not cached IDs
- Open notebooks before including them in persisted order restoration
- Distinguish notebook IDs from document block IDs in client state
When it happens
Trigger: Calling the notebook sort API with an ID for a notebook that is closed, was removed, never existed, or whose ID string is truncated/typo'd; sorting a persisted list saved when more notebooks were open.
Common situations: Client caches notebook IDs from a previous session; notebook closed by another window between listing and sorting; hardcoded ID in a script after the notebook was deleted; mixing up notebook ID with document ID.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/9b0b0ed4e96471e8.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/file.go:2918
openedBoxes := map[string]*Box{}
for _, box := range Conf.GetOpenedBoxes() {
openedBoxes[box.ID] = box
}
notebookPlans := make([]*notebookSortPlan, 0, len(notebookSorts))
notebookIDs := map[string]struct{}{}
for _, item := range notebookSorts {
if nil == item {
return ret, errors.New("notebook sort item must not be nil")
}
if _, ok := notebookIDs[item.ID]; ok {
return ret, fmt.Errorf("duplicate notebook ID [%s]", item.ID)
}
notebookIDs[item.ID] = struct{}{}
box := boxes[item.ID]
if nil == box {
return ret, fmt.Errorf("notebook [%s] not found", item.ID)
}
notebookPlans = append(notebookPlans, ¬ebookSortPlan{item: item, box: box})
}
docPlans := make([]*docSortPlan, 0, len(docSorts))
docIDs := map[string]struct{}{}
for _, item := range docSorts {
if nil == item {
return ret, errors.New("document sort item must not be nil")
}
if _, ok := docIDs[item.ID]; ok {
return ret, fmt.Errorf("duplicate document ID [%s]", item.ID)
}
docIDs[item.ID] = struct{}{}
bt := treenode.GetBlockTree(item.ID)
if nil == bt || nil == openedBoxes[bt.BoxID] {
return ret, fmt.Errorf("document [%s] not found in opened and unlocked notebooks", item.ID)View on GitHub (pinned to 9f775e8a12)