siyuan-note/siyuan · error
Field [ ] should be of type [ ]
Error message
Field [%s] should be of type [%s]
What it means
legacyField decodes a field into a typed value; when json.Unmarshal into the target type fails it reports 'Field [<key>] should be of type [<kind>]'. This is the generic wrong-type error for legacy-decoded requests, telling the caller which field was sent with an incompatible JSON type.
Solutions
- Send the field as the JSON type named in the message (e.g. wrap numbers in quotes for String fields)
- Fix client serialization so the variable's type matches the contract
- Check for double-encoded values (a JSON string containing JSON) which fail decoding into non-string targets
- Consult the API contract in kernel/apicontract/ for each field's expected kind
Example fix
// before
{"id": 12345, "type": "id"}
// after
{"id": "12345", "type": "id"} Defensive patterns
Strategy: type-guard
Validate before calling
const isStr = (v) => typeof v === "string"; if (!isStr(body.id)) body.id = String(body.id); // coerce numeric ids before sending
Type guard
const isString = (v) => typeof v === "string";
const assertString = (o, k) => { if (!isString(o[k])) throw new TypeError(`${k} must be a string`); }; Try / catch
try {
const resp = await fetch(url, {method: "POST", body: JSON.stringify(body)});
const out = await resp.json();
} catch (e) {
if (String(e).includes("should be of type")) {
console.error("Wrong field type:", e.message); // fix the field's JSON type and retry once
} else {
throw e;
}
} Prevention
- Coerce numbers to strings for String-typed fields before serialization
- Enable strict typing in the client (TypeScript interfaces for each request body)
- Avoid booleans/numbers for fields documented as String
When it happens
Trigger: Sending a required field with the wrong JSON type, e.g. "id": 123 instead of a string, "type": true, or an array where a string is expected — for any endpoint decoded via legacyField (inline styles, AI params, AV requests, bazaar, copyFiles, DocVersionRef, graph).
Common situations: Dynamic languages coercing numbers/booleans into the payload; clients sending numeric IDs; copy-pasted payloads mixing types between endpoints.
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
- Field [ ] should be of type [Array]
- Field [scope] should be of type [String]
- block [ ] type is locked: expected , got
- createDocTree definition must be a list
- createDocTree document must be a dictionary
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/1879b7ba134c063c.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/inline_style_input.go:49
return
}
data, err := json.Marshal(normalized)
if err == nil {
err = json.Unmarshal(data, &value)
}
return
}
func legacyField[T any](fields map[string]json.RawMessage, key, kind string, required bool) (value T, err error) {
raw := fields[key]
if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
if required {
err = fmt.Errorf("Field [%s] is required", key)
}
return
}
if json.Unmarshal(raw, &value) != nil {
err = fmt.Errorf("Field [%s] should be of type [%s]", key, kind)
}
return
}
func init() {
SetInlineStyles.decodeRequest = func(reader io.Reader) (request SetInlineStylesRequest, err error) {
fields, err := blockRequestFields(reader, "/api/storage/setInlineStyles")
if err != nil {
return request, err
}
version, err := legacyField[float64](fields, "version", "Number", true)
if err != nil {
return request, err
}
if _, err = legacyField[[]json.RawMessage](fields, "styles", "Array", true); err != nil {
return request, err
}
app, err := legacyField[string](fields, "app", "String", false)View on GitHub (pinned to 9f775e8a12)