{"record":{"id":"9bed492143178729","repo":"siyuan-note/siyuan","slug":"field-s-is-required-multipart","errorCode":null,"errorMessage":"Field [%s] is required","messagePattern":"Field \\[(.+?)\\] is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/multipart.go","lineNumber":86,"sourceCode":"\t\tswitch field.Type {\n\t\tcase reflect.TypeFor[*multipart.FileHeader](), reflect.TypeFor[[]*multipart.FileHeader]():\n\t\t\tfiles := form.File[name]\n\t\t\tif len(files) == 0 {\n\t\t\t\tif !optional {\n\t\t\t\t\treturn request, fmt.Errorf(\"%s not found\", name)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif field.Type.Kind() == reflect.Slice {\n\t\t\t\tvalue.Field(i).Set(reflect.ValueOf(files))\n\t\t\t} else {\n\t\t\t\tvalue.Field(i).Set(reflect.ValueOf(files[0]))\n\t\t\t}\n\t\tcase reflect.TypeFor[string](), reflect.TypeFor[*string]():\n\t\t\tvalues := form.Value[name]\n\t\t\tif len(values) == 0 {\n\t\t\t\tif !optional {\n\t\t\t\t\treturn request, fmt.Errorf(\"Field [%s] is required\", name)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif field.Type.Kind() == reflect.Pointer {\n\t\t\t\ttext := reflect.New(field.Type.Elem())\n\t\t\t\ttext.Elem().SetString(values[0])\n\t\t\t\tvalue.Field(i).Set(text)\n\t\t\t} else {\n\t\t\t\tvalue.Field(i).SetString(values[0])\n\t\t\t}\n\t\tdefault:\n\t\t\treturn request, fmt.Errorf(\"unsupported multipart field: %s\", name)\n\t\t}\n\t}\n\treturn\n}\n","sourceCodeStart":68,"sourceCodeEnd":103,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/multipart.go#L68-L103","documentation":"For string (or *string) fields of the multipart request struct, DecodeMultipart looks up form.Value by the api tag name; if no text value with that name was submitted and the field is not tagged optional, it returns \"Field [<name>] is required\". This is contract-level required-field validation for form text fields.","triggerScenarios":"Submitting a multipart form to an endpoint whose request struct declares a required string field (no ,optional, in the api tag) while the client omits that value or sends it under a different name.","commonSituations":"A required form input was left blank; the client renamed the input's name attribute; API change added a new required field that older clients do not send.","solutions":["Send the missing value in the multipart form under the exact field name from the `api` tag.","If the field is optional, change the struct tag to include ,optional, (e.g. `api:\"note,optional\"`).","Compare the client's submitted field names with the endpoint's request struct and align them."],"exampleFix":"// before\nformData.append(\"title\", \"\"); // or omitted entirely\n// after\nformData.append(\"title\", \"My title\");","handlingStrategy":"validation","validationCode":"function requireField(formData, name) {\n  const v = formData.get(name);\n  if (v == null || v === \"\") throw new Error(\"field '\" + name + \"' is required\");\n}","typeGuard":"const hasNonEmptyValue = (fd, n) => { const v = fd.get(n); return typeof v === \"string\" && v.length > 0; };","tryCatchPattern":"try { await submit(url, formData); } catch (e) { if (/Field \\[(.+)\\] is required/.test(String(e))) { showFieldError(e.match(/\\[(.+)\\]/)[1]); return; } throw e; }","preventionTips":["Validate required form inputs client-side before submit.","Keep input name attributes in sync with the server contract.","When the contract adds a required field, update all clients in the same change."],"tags":["multipart","validation","required-field"],"backgroundTag":"missing-required-argument","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"}