{"record":{"id":"41bdf4c441371a99","repo":"siyuan-note/siyuan","slug":"document-sort-item-must-not-be-nil","errorCode":null,"errorMessage":"document sort item must not be nil","messagePattern":"document sort item must not be nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/file.go","lineNumber":2903,"sourceCode":"\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)\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}","sourceCodeStart":2885,"sourceCodeEnd":2921,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/file.go#L2885-L2921","documentation":"SetFileTreeSort iterates the docSorts array and rejects a nil element with this error before any document is moved. A null entry has no ID or sort weight, so the request is considered malformed and the entire batch sort fails without side effects.","triggerScenarios":"Posting a docSorts array containing a JSON null element to the file-tree sort API, typically from a client that appends undefined entries or serializes a sparse array.","commonSituations":"Plugin builds the doc list with holes (delete leaving undefined in JS arrays); a failed lookup returns null and is pushed into the array unconditionally; JSON produced from Map-to-array conversion with missing keys.","solutions":["Remove null entries from docSorts before sending (filter(Boolean) in JS or equivalent).","Fix the code that populates docSorts so failed lookups are skipped rather than pushed as null.","Validate the payload shape client-side (every item is an object with id and sort) before issuing the request."],"exampleFix":"// before\nconst docs = allDocs.map(d => lookup(d)); // lookup may return null\nfetchPost(\"/api/filetree/setFileTreeSort\", {docSorts: docs});\n// after\nconst docs = allDocs.map(d => lookup(d)).filter(Boolean);\nfetchPost(\"/api/filetree/setFileTreeSort\", {docSorts: docs});","handlingStrategy":"validation","validationCode":"if (docSorts.some(i => i == null)) {\n  throw new Error(\"docSorts contains null entries\");\n}","typeGuard":"const isValidDocItem = (i) => i != null && typeof i.id === \"string\" && /^[0-9]{14}-[a-z0-9]{7}$/.test(i.id) && typeof i.sort === \"number\";","tryCatchPattern":"try {\n  await api.setFileTreeSort({docSorts});\n} catch (e) {\n  if (String(e).includes(\"must not be nil\")) {\n    return api.setFileTreeSort({docSorts: docSorts.filter(Boolean)});\n  }\n  throw e;\n}","preventionTips":["Skip failed lookups instead of pushing null into the doc list","Sanitize arrays loaded from persistent storage (holes become null after JSON round-trip)","Assert payload shape with a schema check before calling the API"],"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"}