{"record":{"id":"82b17c240c45c271","repo":"siyuan-note/siyuan","slug":"field-s-should-be-of-type-s","errorCode":null,"errorMessage":"Field [%s] should be of type [%s]","messagePattern":"Field \\[(.+?)\\] should be of type \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/export.go","lineNumber":253,"sourceCode":"\t\t}\n\t\traw := fields[name]\n\t\tif len(raw) == 0 || bytes.Equal(bytes.TrimSpace(raw), []byte(\"null\")) {\n\t\t\tif has(\"optional\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"Field [%s] is required\", name)\n\t\t}\n\t\tif err := decodeRequestValue(raw, dest); err != nil {\n\t\t\tif has(\"ignoretype\") || value.Type() == reflect.TypeFor[ExportTitleOptions]() && name == \"addTitle\" {\n\t\t\t\tdest.SetZero()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tkind := dest.Kind()\n\t\t\tif kind == reflect.Pointer {\n\t\t\t\tkind = dest.Type().Elem().Kind()\n\t\t\t}\n\t\t\tkindName := map[reflect.Kind]string{reflect.String: \"String\", reflect.Bool: \"Boolean\", reflect.Float64: \"Number\", reflect.Slice: \"Array\"}[kind]\n\t\t\treturn fmt.Errorf(\"Field [%s] should be of type [%s]\", name, kindName)\n\t\t}\n\t\tif has(\"trim\") {\n\t\t\ttext := strings.TrimSpace(dest.String())\n\t\t\tif text == \"\" {\n\t\t\t\treturn fmt.Errorf(\"Field [%s] must not be empty\", name)\n\t\t\t}\n\t\t\tdest.SetString(text)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc bindExportDecoder[Request, Data any](endpoint *Endpoint[Request, Data]) {\n\tendpoint.decodeRequest = func(reader io.Reader) (request Request, err error) {\n\t\tfields, err := blockRequestFields(reader, endpoint.Definition().Path)\n\t\tif err != nil {\n\t\t\treturn request, err\n\t\t}","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/export.go#L235-L271","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n{ id: 12345, addTitle: \"true\" }\n// after\n{ id: \"12345\", addTitle: true }","handlingStrategy":"type-guard","validationCode":"const kindCheck = { String: (v) => typeof v === \"string\", Boolean: (v) => typeof v === \"boolean\", Number: (v) => typeof v === \"number\", Array: (v) => Array.isArray(v) };\nfunction checkTypes(payload, schema) {\n  for (const [name, kind] of Object.entries(schema)) {\n    if (payload[name] !== undefined && !kindCheck[kind](payload[name])) throw new Error(`Field [${name}] should be of type [${kind}]`);\n  }\n}\ncheckTypes(payload, { id: \"String\", addTitle: \"Boolean\" });","typeGuard":"const isJsonType = (v, kind) => ({ String: typeof v === \"string\", Boolean: typeof v === \"boolean\", Number: typeof v === \"number\", Array: Array.isArray(v) })[kind];","tryCatchPattern":"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]}`); }","preventionTips":["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.)"],"tags":["api","validation","type-mismatch","export"],"backgroundTag":"type-mismatch","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}