{"record":{"id":"f6adcad15020e923","repo":"siyuan-note/siyuan","slug":"document-s-not-found-in-opened-and-unlocked-not","errorCode":null,"errorMessage":"document [%s] not found in opened and unlocked notebooks","messagePattern":"document \\[(.+?)\\] not found in opened and unlocked notebooks","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/file.go","lineNumber":2612,"sourceCode":"\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)\n\t\t}\n\t\tif bt.ID != bt.RootID || \"d\" != bt.Type || IsBoxDoc(bt.BoxID, bt.RootID) {\n\t\t\treturn ret, fmt.Errorf(\"block [%s] is not a sortable document\", item.ID)\n\t\t}\n\t\tif nil == boxes[bt.BoxID] {\n\t\t\treturn ret, fmt.Errorf(\"notebook [%s] not found for document [%s]\", bt.BoxID, item.ID)\n\t\t}\n\t\tdocPlans = append(docPlans, &docSortPlan{item: item, boxID: bt.BoxID, parentPath: path.Dir(bt.Path)})\n\t}\n\n\tdocGroups := map[string]*docSortGroup{}\n\tfor _, plan := range docPlans {\n\t\tgroup := docGroups[plan.boxID]\n\t\tif nil == group {\n\t\t\tconfPath := filepath.Join(util.DataDir, plan.boxID, \".siyuan\", \"sort.json\")\n\t\t\tfullSortIDs, readErr := readSortConfMap(confPath)\n\t\t\tif readErr != nil {\n\t\t\t\treturn ret, readErr","sourceCodeStart":2594,"sourceCodeEnd":2630,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/file.go#L2594-L2630","documentation":"Thrown by SetFileTreeSort when a document sort item fails one of two conditions: treenode.GetBlockTree(item.ID) returns nil (the block does not exist in the block tree), or the block's notebook (bt.BoxID) is not present in the openedBoxes map (built from Conf.GetOpenedBoxes()). Only documents in notebooks that are currently open and unlocked can have their sort order persisted.","triggerScenarios":"Calling POST /api/filetree/setFileTreeSort with a docSorts entry whose ID does not exist in the block tree (deleted or invalid block), or whose containing notebook is closed or locked. The check `nil == openedBoxes[bt.BoxID]` catches closed notebooks and locked/encrypted notebooks since GetOpenedBoxes only returns open, unlocked boxes.","commonSituations":"Document was deleted in another session but the current tab's file tree still shows it; the notebook containing the document was closed (not just collapsed — fully closed) after the tree was loaded; an encrypted notebook was locked after the tree UI was rendered; an external API client using a stale or fabricated block ID.","solutions":["Ensure the notebook containing the document is open and unlocked — check via GET /api/notebook/lsNotebooks and open it if closed.","If the document was deleted, refresh the file tree so the UI no longer references the stale block ID.","If building an API client, verify each docSorts ID exists via treenode.GetBlockTree or a block lookup endpoint before submitting the sort payload."],"exampleFix":"// before\nawait post('/api/filetree/setFileTreeSort', { notebookSorts: [], docSorts })\n\n// after\nconst { boxes } = await get('/api/notebook/lsNotebooks')\nconst openBoxIds = new Set(boxes.filter(b => !b.closed).map(b => b.id))\n// only submit docs whose notebook is currently open\nconst safeDocSorts = docSorts.filter(item => openBoxIds.has(docNotebookMap[item.id]))\nawait post('/api/filetree/setFileTreeSort', { notebookSorts: [], docSorts: safeDocSorts })","handlingStrategy":"validation","validationCode":"// Only submit doc sort items for documents in open, unlocked notebooks\nconst { boxes } = await get('/api/notebook/lsNotebooks')\nconst openBoxIds = new Set(boxes.filter(b => !b.closed).map(b => b.id))\n// For each docSorts item, verify its notebook is open before submitting\nconst safeDocSorts = docSorts.filter(item => {\n  const bt = blockTreeCache[item.id]\n  return bt && openBoxIds.has(bt.boxID)\n})","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check that the document's notebook is open and unlocked before including it in a sort payload.","Listen for notebook close/lock WebSocket events and update UI state to prevent stale sort submissions.","Handle document deletion events to remove stale block IDs from the file tree."],"tags":["validation","filetree","sort","notebook-state","stale-state"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}