{"record":{"id":"9b0b0ed4e96471e8","repo":"siyuan-note/siyuan","slug":"notebook-s-not-found-9b0b0e","errorCode":null,"errorMessage":"notebook [%s] not found","messagePattern":"notebook \\[(.+?)\\] not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/file.go","lineNumber":2594,"sourceCode":"\topenedBoxes := map[string]*Box{}\n\tfor _, box := range Conf.GetOpenedBoxes() {\n\t\topenedBoxes[box.ID] = box\n\t}\n\n\tnotebookPlans := make([]*notebookSortPlan, 0, len(notebookSorts))\n\tnotebookIDs := map[string]struct{}{}\n\tfor _, item := range notebookSorts {\n\t\tif nil == item {\n\t\t\treturn ret, errors.New(\"notebook sort item must not be nil\")\n\t\t}\n\t\tif _, ok := notebookIDs[item.ID]; ok {\n\t\t\treturn ret, fmt.Errorf(\"duplicate notebook ID [%s]\", item.ID)\n\t\t}\n\t\tnotebookIDs[item.ID] = struct{}{}\n\n\t\tbox := boxes[item.ID]\n\t\tif nil == box {\n\t\t\treturn ret, fmt.Errorf(\"notebook [%s] not found\", item.ID)\n\t\t}\n\t\tnotebookPlans = append(notebookPlans, &notebookSortPlan{item: item, box: box})\n\t}\n\n\tdocPlans := make([]*docSortPlan, 0, len(docSorts))\n\tdocIDs := map[string]struct{}{}\n\tfor _, item := range docSorts {\n\t\tif nil == item {\n\t\t\treturn ret, errors.New(\"document sort item must not be nil\")\n\t\t}\n\t\tif _, ok := docIDs[item.ID]; ok {\n\t\t\treturn ret, fmt.Errorf(\"duplicate document ID [%s]\", item.ID)\n\t\t}\n\t\tdocIDs[item.ID] = struct{}{}\n\n\t\tbt := treenode.GetBlockTree(item.ID)\n\t\tif nil == bt || nil == openedBoxes[bt.BoxID] {\n\t\t\treturn ret, fmt.Errorf(\"document [%s] not found in opened and unlocked notebooks\", item.ID)","sourceCodeStart":2576,"sourceCodeEnd":2612,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/file.go#L2576-L2612","documentation":"Thrown by SetFileTreeSort when a SortItem in notebookSorts references a notebook ID that does not exist in the current configuration (the boxes map, built from Conf.GetBoxes()). This means the client is trying to persist a sort order for a notebook that has been deleted, was never created, or whose ID was fabricated.","triggerScenarios":"Calling POST /api/filetree/setFileTreeSort with a notebookSorts entry whose id does not match any notebook returned by Conf.GetBoxes(). Common when a notebook was deleted in another session/tab but the file tree UI in the current session still holds the stale ID and includes it in a sort payload.","commonSituations":"Stale UI state after a notebook was removed via a different device or sync; an external API client using a hardcoded or cached notebook ID that no longer exists; race condition where a notebook is deleted between the UI loading the tree and the user reordering it.","solutions":["Refresh the notebook list from the kernel (e.g. GET /api/notebook/lsNotebooks) before attempting to persist sort order, so the UI only sends IDs that currently exist.","If building an API client, validate each notebookSorts ID against the list of known box IDs returned by the notebook listing endpoint before calling setFileTreeSort.","If the error persists, check whether the notebook was deleted by another session or via sync — reload the workspace configuration."],"exampleFix":"// before\nawait post('/api/filetree/setFileTreeSort', { notebookSorts, docSorts: [] })\n\n// after\nconst { notebooks } = await get('/api/notebook/lsNotebooks')\nconst validIds = new Set(notebooks.map(n => n.id))\nconst filtered = notebookSorts.filter(item => validIds.has(item.id))\nawait post('/api/filetree/setFileTreeSort', { notebookSorts: filtered, docSorts: [] })","handlingStrategy":"validation","validationCode":"// Validate notebook IDs exist before persisting sort order\nconst { notebooks } = await get('/api/notebook/lsNotebooks')\nconst validBoxIds = new Set(notebooks.map(n => n.id))\nconst validNotebookSorts = notebookSorts.filter(item => validBoxIds.has(item.id))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Refresh the notebook list from the kernel before persisting sort order.","Handle notebook deletion events (WebSocket push) to remove stale IDs from UI state.","In multi-session setups, re-sync the notebook list after long idle periods."],"tags":["validation","filetree","sort","notebook","stale-state"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}