siyuan-note/siyuan · error
Field [level] should be of type [Number]
Error message
Field [level] should be of type [Number]
What it means
The /api/block/getHeadingLevelTransaction request requires a numeric level field. The kernel returns this error when level is missing, an explicit null, or not a JSON number (string, boolean, object). The level selects the target heading level for the heading-conversion transaction.
Solutions
- Send level as a JSON number: "level": 2 (1-6 for heading levels)
- Convert string input with Number(level) before building the payload
- Always include level in the payload alongside ids or id
Example fix
// before
{ "ids": ["20240101120000-abcdefg"], "level": "3" }
// after
{ "ids": ["20240101120000-abcdefg"], "level": 3 } Defensive patterns
Strategy: validation
Validate before calling
const levelNum = Number(level);
if (!Number.isFinite(levelNum)) throw new TypeError('level must be a number');
payload.level = levelNum; Type guard
const isHeadingLevel = (v) => typeof v === 'number' && Number.isInteger(v) && v >= 1 && v <= 6;
Try / catch
try { await fetchPost('/api/block/getHeadingLevelTransaction', payload); } catch (e) { if (/Field \[level\] should be of type \[Number\]/.test(e?.message)) { payload.level = Number(payload.level); /* retry */ } } Prevention
- Send level as a JSON number, never a quoted string
- Coerce dropdown/select values with Number() before building the payload
- Type level as number in the request interface to catch this at compile time
When it happens
Trigger: POST /api/block/getHeadingLevelTransaction with {"ids":[...]} but no level; "level":"2" (string instead of number); "level":null.
Common situations: Dropdown UI binding the level as a string option value; omitting level because older examples only showed ids; JSON forms serializing numeric inputs as strings.
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
- block [ ] type is locked: expected , got
- Field [notebook] should be of type [String]
- Field [preview] should be of type [Boolean]
- Field [ ] should be of type [Array]
- Field [ ] should be of type [ ]
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/eeb6c41084a3d2df.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/block_remaining.go:159
return request, errors.New("Field [notebook] is required")
}
if request.Scope == "notebook" {
request.Notebook = strings.TrimSpace(request.Notebook)
if request.Notebook == "" {
return request, errors.New("Field [notebook] must not be empty")
}
}
_ = json.Unmarshal(fields["id"], &request.ID)
return
}
func decodeHeadingLevel(reader io.Reader) (request HeadingLevelRequest, err error) {
fields, err := blockRequestFields(reader, "/api/block/getHeadingLevelTransaction")
if err != nil {
return request, err
}
if len(fields["level"]) == 0 || bytes.Equal(fields["level"], []byte("null")) || json.Unmarshal(fields["level"], &request.Level) != nil {
return request, errors.New("Field [level] should be of type [Number]")
}
var entries []json.RawMessage
if raw := fields["ids"]; len(raw) > 0 && !bytes.Equal(raw, []byte("null")) && json.Unmarshal(raw, &entries) == nil {
request.IDs = []string{}
seen := map[string]bool{}
for _, entry := range entries {
var id string
if bytes.Equal(entry, []byte("null")) || json.Unmarshal(entry, &id) != nil {
return request, errors.New("Field [ids] should contain strings")
}
if !seen[id] {
request.IDs = append(request.IDs, id)
seen[id] = true
}
}
} else if len(fields["id"]) == 0 || bytes.Equal(fields["id"], []byte("null")) || json.Unmarshal(fields["id"], &request.ID) != nil {
return request, errors.New("Field [id] should be of type [String]")
}View on GitHub (pinned to 9f775e8a12)