siyuan-note/siyuan · error
Field [sortMode] must be an integer or null
Error message
Field [sortMode] must be an integer or null: %s
What it means
Once present, the raw 'sortMode' value must unmarshal into a Go int or be JSON null. Anything else (string "7", bool, object, float with fraction) fails json.Unmarshal into int and produces this message with the underlying decoder error appended. The library is strict because sortMode maps directly to an enum-like integer in the kernel config.
Solutions
- Convert the value to an integer before sending, e.g. parseInt(mode, 10)
- Send null explicitly if clearing the mode is intended
- Consult the API docs for the valid integer sort-mode codes
Example fix
// before
fetchPost("/api/filetree/setDocSortMode", { id, sortMode: String(mode) });
// after
fetchPost("/api/filetree/setDocSortMode", { id, sortMode: Number.parseInt(mode, 10) }); Defensive patterns
Strategy: type-guard
Validate before calling
if (!Number.isInteger(sortMode)) throw new TypeError(`sortMode must be an integer, got ${typeof sortMode}`); Type guard
const isSortMode = (v) => v === null || Number.isInteger(v);
Try / catch
try {
await fetchPost("/api/filetree/setDocSortMode", { id, sortMode });
} catch (e) {
if (String(e.message).includes("must be an integer or null")) {
await fetchPost("/api/filetree/setDocSortMode", { id, sortMode: parseInt(sortMode, 10) });
}
} Prevention
- Parse string modes with parseInt/Number before sending
- Never send float or object values for enum-like integer fields
- Type the payload in TypeScript to catch coercion at compile time
When it happens
Trigger: POST /api/filetree/setDocSortMode with {"id":"...","sortMode":"7"} or sortMode: 1.5 or sortMode: {"mode":7}.
Common situations: JavaScript clients that keep the mode as a numeric string from a select element; JSON produced by template interpolation without type conversion; API consumers guessing the field is a string code like "name".
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- attr must be a string or null (got %T)
- block [ ] type is locked: expected , got
- createDocTree definition must be a list
- createDocTree document must be a dictionary
- date display format is only available for date fields
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/06b7baa96c52ba65.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/filetree_input.go:130
delete(fields, "querySubTypes")
if value, ok := filtered["subTypes"]; ok {
fields["querySubTypes"] = value
}
}
return fileTreeBind[FileTreeGetDocOptions](fields)
}
func (r FileTreeSortModeRequest) Mode() (*int, error) {
raw := r.sortModeFields["sortMode"]
if len(raw) == 0 {
return nil, fmt.Errorf("Field [sortMode] is required")
}
if bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return nil, nil
}
var value int
if err := json.Unmarshal(raw, &value); err != nil {
return nil, fmt.Errorf("Field [sortMode] must be an integer or null: %s", err)
}
return &value, nil
}
func init() {
fileTreeJSONDecoder(&MoveLocalShorthands)
fileTreeJSONDecoder(&UpsertIndexes)
fileTreeJSONDecoder(&RemoveIndexes)
fileTreeJSONDecoder(&Doc2Heading)
fileTreeJSONDecoder(&GetHPathByPath)
fileTreeJSONDecoder(&GetHPathsByPaths)
fileTreeJSONDecoder(&GetHPathByID)
fileTreeJSONDecoder(&GetFullHPathByID)
fileTreeJSONDecoder(&MoveDocs)
fileTreeJSONDecoder(&MoveDocsByID)
fileTreeJSONDecoder(&RemoveDoc)
fileTreeJSONDecoder(&RemoveDocs)
fileTreeJSONDecoder(&RenameDoc)View on GitHub (pinned to 9f775e8a12)