siyuan-note/siyuan · error

Field [ ] should be of type [ ]

Error message

Field [%s] should be of type [%s]

What it means

decodeExportFields validates each decoded export field against the declared kind: string→String, bool→Boolean, float64→Number, slice→Array. If the submitted JSON value has the wrong JSON type for the field (after pointer dereferencing), it returns "Field [%s] should be of type [%s]" with the human-readable type name. The kindName map has no entry for other kinds, so unsupported kinds yield an empty type name — but the error still fires for any type mismatch.

Solutions

  1. Convert the field to the declared JSON type named in the error (String/Boolean/Number/Array)
  2. Use JSON.stringify-compatible values: true/false not "true", numbers not numeric strings
  3. Wrap scalar values in arrays when the contract expects an Array
  4. Check the endpoint contract struct in kernel/apicontract/ for exact field types

Example fix

// before
{ id: 12345, addTitle: "true" }
// after
{ id: "12345", addTitle: true }
Defensive patterns

Strategy: type-guard

Validate before calling

const kindCheck = { String: (v) => typeof v === "string", Boolean: (v) => typeof v === "boolean", Number: (v) => typeof v === "number", Array: (v) => Array.isArray(v) };
function checkTypes(payload, schema) {
  for (const [name, kind] of Object.entries(schema)) {
    if (payload[name] !== undefined && !kindCheck[kind](payload[name])) throw new Error(`Field [${name}] should be of type [${kind}]`);
  }
}
checkTypes(payload, { id: "String", addTitle: "Boolean" });

Type guard

const isJsonType = (v, kind) => ({ String: typeof v === "string", Boolean: typeof v === "boolean", Number: typeof v === "number", Array: Array.isArray(v) })[kind];

Try / catch

try { await exportApi(payload); } catch (e) { const m = String(e).match(/Field \[(\w+)\] should be of type \[(\w+)\]/); if (m) console.error(`Fix ${m[1]} to ${m[2]}`); }

Prevention

When it happens

Trigger: Calling an export API with a field whose JSON type disagrees with the contract: e.g. addTitle: "yes" (string for a Boolean), id: 12345 (number for a String), options: "pdf" (string where an Array/Object is expected), or null nested inside a non-nullable scalar.

Common situations: JS code coercing booleans to strings; numeric IDs losing quotes; copy-pasted payloads from docs of a different endpoint; form/multipart values (always strings) sent where arrays are required.

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/82b17c240c45c271. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/export.go:253

		}
		raw := fields[name]
		if len(raw) == 0 || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
			if has("optional") {
				continue
			}
			return fmt.Errorf("Field [%s] is required", name)
		}
		if err := decodeRequestValue(raw, dest); err != nil {
			if has("ignoretype") || value.Type() == reflect.TypeFor[ExportTitleOptions]() && name == "addTitle" {
				dest.SetZero()
				continue
			}
			kind := dest.Kind()
			if kind == reflect.Pointer {
				kind = dest.Type().Elem().Kind()
			}
			kindName := map[reflect.Kind]string{reflect.String: "String", reflect.Bool: "Boolean", reflect.Float64: "Number", reflect.Slice: "Array"}[kind]
			return fmt.Errorf("Field [%s] should be of type [%s]", name, kindName)
		}
		if has("trim") {
			text := strings.TrimSpace(dest.String())
			if text == "" {
				return fmt.Errorf("Field [%s] must not be empty", name)
			}
			dest.SetString(text)
		}
	}
	return nil
}

func bindExportDecoder[Request, Data any](endpoint *Endpoint[Request, Data]) {
	endpoint.decodeRequest = func(reader io.Reader) (request Request, err error) {
		fields, err := blockRequestFields(reader, endpoint.Definition().Path)
		if err != nil {
			return request, err
		}

View on GitHub (pinned to 9f775e8a12)