siyuan-note/siyuan · error
Field [ids] should contain strings
Error message
Field [ids] should contain strings
What it means
In /api/block/getHeadingLevelTransaction, the optional ids array is validated element-by-element: every element must be a JSON string (null elements are also rejected, unlike some other endpoints). The kernel raises this error when any element fails to unmarshal into a string. This branch only runs when ids is present and is a valid JSON array.
Solutions
- Ensure every element is a plain JSON string block ID
- Filter or map non-strings: ids.filter(id => typeof id === 'string' && id)
- If a single block is targeted, use the scalar "id" field instead of the ids array
Example fix
// before
await fetchPost("/api/block/getHeadingLevelTransaction", { ids: [id1, id2], level: 2 }); // id2 is a number
// after
await fetchPost("/api/block/getHeadingLevelTransaction", { ids: [id1, String(id2)], level: 2 }); Defensive patterns
Strategy: validation
Validate before calling
if (payload.ids && !payload.ids.every(id => typeof id === 'string' && id)) throw new TypeError('ids must all be non-empty strings'); Type guard
const isBlockIdArray = (v) => Array.isArray(v) && v.every((x) => typeof x === 'string' && /^\d{14}-[0-9a-z]{7}$/.test(x)); Try / catch
try { await fetchPost('/api/block/getHeadingLevelTransaction', payload); } catch (e) { if (/Field \[ids\] should contain strings/.test(e?.message)) { payload.ids = payload.ids.filter(id => typeof id === 'string' && id); /* retry */ } } Prevention
- Map all ID sources through String() before pushing into the ids array
- Filter out null/undefined before serializing sparse arrays
- Validate the block-ID format (\d{14}-[0-9a-z]{7}) at collection time
When it happens
Trigger: POST /api/block/getHeadingLevelTransaction with "ids": ["id1", 42], "ids": [null], or "ids": [{"id":"..."}].
Common situations: Mixing numeric row IDs from a database view with block ID strings; null elements appearing after JSON round-trips; passing objects copied from another API's response shape.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Field [ ] should contain non-empty strings
- AI editor action must not be empty
- block [ ] type is locked: expected , got
- block swap requires two non-document blocks
- Bookmark cannot be empty
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/9a10191e28687eb3.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/block_remaining.go:168
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]")
}
return
}
View on GitHub (pinned to 9f775e8a12)