{"record":{"id":"fb8a715f13acc50e","repo":"siyuan-note/siyuan","slug":"s-not-found","errorCode":null,"errorMessage":"%s not found","messagePattern":"(.+?) not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/multipart.go","lineNumber":73,"sourceCode":"\t\tvalue.Set(reflect.ValueOf(MultipartFields{Value: form.Value, File: form.File}))\n\t\treturn\n\t}\n\tif value.Kind() != reflect.Struct {\n\t\treturn request, fmt.Errorf(\"multipart request must be a struct\")\n\t}\n\tif err = validateMultipartRequest(value.Type()); err != nil {\n\t\treturn\n\t}\n\tfor i := 0; i < value.NumField(); i++ {\n\t\tfield := value.Type().Field(i)\n\t\tname := strings.Split(field.Tag.Get(\"json\"), \",\")[0]\n\t\toptional := strings.Contains(\",\"+field.Tag.Get(\"api\")+\",\", \",optional,\")\n\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())","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/multipart.go#L55-L91","documentation":"When mapping multipart form parts onto the request struct, a field typed *multipart.FileHeader (or a slice of them) must be present in form.File under its api tag name unless the tag includes ,optional,. If the named file part is absent, decoding fails with \"<name> not found\". It enforces required file uploads at the contract layer.","triggerScenarios":"POSTing to a multipart endpoint without appending a file under the exact field name declared by the struct's `api` tag (e.g. the struct requires \"file\" but the client sent \"upload\").","commonSituations":"Client uses a different form field name than the contract; file input left empty in the UI; contract changed the field name while the client still uses the old one.","solutions":["Append the file to FormData under the exact field name from the endpoint's request struct `api` tag.","If the file is genuinely optional, mark the struct field's api tag as `api:\"name,optional\"`.","Check server logs / the contract definition to confirm the expected field name, then align the client."],"exampleFix":"// before\nformData.append(\"upload\", fileObj) // struct expects \"file\"\n// after\nformData.append(\"file\", fileObj)","handlingStrategy":"validation","validationCode":"function requireFile(formData, name) {\n  const f = formData.get(name);\n  if (f == null || typeof f === \"string\") throw new Error(\"file part '\" + name + \"' is required\");\n}","typeGuard":"const isUploadedFile = (v) => v != null && typeof v !== \"string\"; // File/Blob in browser","tryCatchPattern":"try { await upload(url, formData); } catch (e) { if (String(e).includes(\"not found\")) { throw new Error(\"missing required file part — check field name matches the api tag\"); } throw e; }","preventionTips":["Keep client FormData field names identical to the server struct's api tags.","Mark truly optional file fields with ,optional, in the api tag.","Check the file input is non-empty before submitting."],"tags":["multipart","file-upload","validation"],"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"}