{"record":{"id":"adeb179a185d798d","repo":"siyuan-note/siyuan","slug":"notebook-sort-item-must-not-be-nil","errorCode":null,"errorMessage":"notebook sort item must not be nil","messagePattern":"notebook sort item must not be nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/file.go","lineNumber":2885,"sourceCode":"\t}\n\n\tFlushTxQueue()\n\tfileTreeSortLock.Lock()\n\tdefer fileTreeSortLock.Unlock()\n\tboxes := map[string]*Box{}\n\tfor _, box := range Conf.GetBoxes() {\n\t\tboxes[box.ID] = box\n\t}\n\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\")","sourceCodeStart":2867,"sourceCodeEnd":2903,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/file.go#L2867-L2903","documentation":"SetFileTreeSort builds notebookSortPlan entries from the request's notebookSorts array and rejects a nil element outright. A null entry in the JSON array cannot carry an ID or sort weight, so the entire batch sort fails fast with this error before any file is moved.","triggerScenarios":"Posting to the file-tree sort API a notebookSorts array containing a JSON null element, e.g. [{\"id\":\"20240101120000-abc\",\"sort\":1},null], usually from a client-side array construction bug.","commonSituations":"Plugin or script building the sort payload by pushing undefined/null items (e.g. array built with holes, JSON.stringify of [undefined]); deserialization of sparse arrays from storage.","solutions":["Inspect the request payload and remove null entries from the notebookSorts array before sending.","Fix the client code that constructs the array to filter out undefined/null values (e.g. arr.filter(Boolean)).","If a notebook was deleted, drop it from the sort list instead of sending a placeholder null."],"exampleFix":"// before\nfetchPost(\"/api/filetree/setFileTreeSort\", {notebookSorts: [ ...items, removedNotebook ]})\n// after\nconst items = [...items, removedNotebook].filter(Boolean);\nfetchPost(\"/api/filetree/setFileTreeSort\", {notebookSorts: items})","handlingStrategy":"validation","validationCode":"if (notebookSorts.some(i => i == null)) {\n  throw new Error(\"notebookSorts contains null entries\");\n}","typeGuard":"const isValidItem = (i) => i != null && typeof i.id === \"string\" && i.id.length > 0 && typeof i.sort === \"number\";","tryCatchPattern":"try {\n  await api.setFileTreeSort({notebookSorts});\n} catch (e) {\n  if (String(e).includes(\"must not be nil\")) {\n    return api.setFileTreeSort({notebookSorts: notebookSorts.filter(Boolean)});\n  }\n  throw e;\n}","preventionTips":["Always filter arrays with .filter(Boolean) before serializing the sort payload","Avoid building arrays with sparse holes (use splice or explicit removal)","Validate payload items have id and sort fields before sending"],"tags":["file-tree","sort","null-argument","request-payload"],"backgroundTag":"null-argument","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}