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
- Convert the field to the declared JSON type named in the error (String/Boolean/Number/Array)
- Use JSON.stringify-compatible values: true/false not "true", numbers not numeric strings
- Wrap scalar values in arrays when the contract expects an Array
- 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
- Send native JSON types: booleans as true/false, numbers unquoted
- Do not stringify values that the contract expects as arrays
- Use TypeScript interfaces mirroring the contract structs
- Test export calls with a type-validating schema (zod etc.)
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
- block [ ] type is locked: expected , got
- Field [level] should be of type [Number]
- Field [notebook] should be of type [String]
- Field [preview] should be of type [Boolean]
- Field [ ] is required
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)