{"record":{"id":"14bec739b94e0e4c","repo":"siyuan-note/siyuan","slug":"field-sortmode-is-required","errorCode":null,"errorMessage":"Field [sortMode] is required","messagePattern":"Field \\[sortMode\\] is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/filetree_input.go","lineNumber":123,"sourceCode":"\t// 文档查询复用分组筛选的兼容规则，未知分组和非布尔选项不会生效。\n\tfilter, _ := json.Marshal(map[string]json.RawMessage{\"subTypes\": fields[\"querySubTypes\"]})\n\tif fields[\"querySubTypes\"] != nil {\n\t\tfiltered, err := searchQueryFields(bytes.NewReader(filter), \"/api/filetree/getDoc\")\n\t\tif err != nil {\n\t\t\treturn FileTreeGetDocOptions{}, err\n\t\t}\n\t\tdelete(fields, \"querySubTypes\")\n\t\tif value, ok := filtered[\"subTypes\"]; ok {\n\t\t\tfields[\"querySubTypes\"] = value\n\t\t}\n\t}\n\treturn fileTreeBind[FileTreeGetDocOptions](fields)\n}\n\nfunc (r FileTreeSortModeRequest) Mode() (*int, error) {\n\traw := r.sortModeFields[\"sortMode\"]\n\tif len(raw) == 0 {\n\t\treturn nil, fmt.Errorf(\"Field [sortMode] is required\")\n\t}\n\tif bytes.Equal(bytes.TrimSpace(raw), []byte(\"null\")) {\n\t\treturn nil, nil\n\t}\n\tvar value int\n\tif err := json.Unmarshal(raw, &value); err != nil {\n\t\treturn nil, fmt.Errorf(\"Field [sortMode] must be an integer or null: %s\", err)\n\t}\n\treturn &value, nil\n}\n\nfunc init() {\n\tfileTreeJSONDecoder(&MoveLocalShorthands)\n\tfileTreeJSONDecoder(&UpsertIndexes)\n\tfileTreeJSONDecoder(&RemoveIndexes)\n\tfileTreeJSONDecoder(&Doc2Heading)\n\tfileTreeJSONDecoder(&GetHPathByPath)\n\tfileTreeJSONDecoder(&GetHPathsByPaths)","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/filetree_input.go#L105-L141","documentation":"The setDocSortMode contract requires a 'sortMode' field in the request body. The Mode() accessor reads the stored raw JSON for the key and, if the key is absent (or was an empty raw value), returns 'Field [sortMode] is required'. Unlike some optional fields, null is accepted (returning nil), but the key must be present.","triggerScenarios":"POST /api/filetree/setDocSortMode with a body that has id but no sortMode key, e.g. {\"id\":\"20240101120000-abcdefg\"}.","commonSituations":"Older clients written before sortMode was required; copy-pasted request bodies missing a field; a UI toggle that serializes only when touched.","solutions":["Add sortMode to the request body as an integer or null","Use a documented sort mode value (e.g. 0 for by name, per API docs)","If the sort should be cleared, send \"sortMode\": null rather than omitting the field"],"exampleFix":"// before\nfetchPost(\"/api/filetree/setDocSortMode\", { id: \"20240101120000-abcdefg\" });\n// after\nfetchPost(\"/api/filetree/setDocSortMode\", { id: \"20240101120000-abcdefg\", sortMode: 0 });","handlingStrategy":"validation","validationCode":"if (!Number.isInteger(sortMode) && sortMode !== null) throw new Error(\"sortMode must be an integer or null\");\nif (!id?.trim()) throw new Error(\"id must not be empty\");\nconst body = { id, sortMode }; // key always present","typeGuard":"const hasSortMode = (b) => \"sortMode\" in b && (b.sortMode === null || Number.isInteger(b.sortMode));","tryCatchPattern":null,"preventionTips":["Always include the sortMode key, even when the value is null","Convert UI select values to integers before sending","Consult API docs for valid sort mode codes"],"tags":["api-request","required-field","filetree"],"backgroundTag":"missing-required-argument","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}