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

  1. Send the field as the JSON type named in the message (e.g. wrap numbers in quotes for String fields)
  2. Fix client serialization so the variable's type matches the contract
  3. Check for double-encoded values (a JSON string containing JSON) which fail decoding into non-string targets
  4. 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

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


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)