siyuan-note/siyuan · error
multipart form is missing
Error message
multipart form is missing
What it means
DecodeMultipart requires a non-nil *multipart.Form; a nil form means the request carried no parseable multipart content. The decoder returns this error rather than dereferencing nil. It usually indicates the request body was empty or not actually multipart/form-data despite hitting a multipart endpoint.
Solutions
- Ensure the client sends a real multipart/form-data body with at least one field or file.
- Set the correct Content-Type with boundary (let fetch/axios/curl -F build it automatically instead of setting the header manually).
- Verify server-side middleware actually parsed the request into *multipart.Form before calling DecodeMultipart.
Example fix
// before
fetch(url, {method: "POST", headers: {"Content-Type": "multipart/form-data"}, body: formData}) // boundary lost
// after
fetch(url, {method: "POST", body: formData}) // browser sets Content-Type + boundary Defensive patterns
Strategy: validation
Validate before calling
function checkMultipartBody(formData) {
if (!(formData instanceof FormData) || [...formData.entries()].length === 0) {
throw new Error("multipart body is empty");
}
} Type guard
const hasEntries = (f) => f != null && typeof f.entries === "function" && [...f.entries()].length > 0;
Try / catch
try { await api.post(url, formData); } catch (e) { if (String(e).includes("multipart form is missing")) { throw new Error("request body was empty or not multipart/form-data: " + e.message); } throw e; } Prevention
- Always append at least one field/file before sending FormData.
- Let fetch/axios generate the multipart Content-Type with boundary.
- Verify HTML forms use enctype="multipart/form-data".
When it happens
Trigger: Calling a multipart endpoint with an empty body, with Content-Type not set to multipart/form-data (so the Gin parser produced no form), or programmatically invoking DecodeMultipart(nil).
Common situations: Client forgot to append any fields/files to FormData; missing enctype on an HTML form; a proxy stripped the multipart body; boundary parameter missing from Content-Type.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- endpoint does not accept multipart data
- open form part [ ] file [ ] error
- read form part [ ] file [ ] error
- asset path [ ] does not match data path [ ]
- authentication probe returned HTTP " + response.status
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/e8973a8725de94bc.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/multipart.go:43
if field.Type != reflect.TypeFor[string]() && field.Type != reflect.TypeFor[*string]() && field.Type != reflect.TypeFor[*multipart.FileHeader]() && field.Type != reflect.TypeFor[[]*multipart.FileHeader]() {
return fmt.Errorf("unsupported multipart field: %s", field.Name)
}
for _, option := range strings.Split(field.Tag.Get("api"), ",") {
if option != "" && option != "optional" && option != "nonnullable" {
return fmt.Errorf("unsupported multipart option: %s", option)
}
}
}
return nil
}
// DecodeMultipart 保留表单重复字段取首值的行为,文件内容由业务入口按需读取。
func (e Endpoint[Request, Data]) DecodeMultipart(form *multipart.Form) (request Request, err error) {
if e.definition.Body != MultipartBody && e.definition.Body != FormBody {
return request, fmt.Errorf("endpoint does not accept multipart data")
}
if form == nil {
return request, fmt.Errorf("multipart form is missing")
}
value := reflect.ValueOf(&request).Elem()
if value.Type() == reflect.TypeFor[ExtensionCopyRequest]() {
decoded, decodeErr := decodeExtensionCopyForm(form)
if decodeErr != nil {
return request, decodeErr
}
value.Set(reflect.ValueOf(decoded))
return
}
if value.Type() == reflect.TypeFor[MultipartFields]() {
value.Set(reflect.ValueOf(MultipartFields{Value: form.Value, File: form.File}))
return
}
if value.Kind() != reflect.Struct {
return request, fmt.Errorf("multipart request must be a struct")
}
if err = validateMultipartRequest(value.Type()); err != nil {View on GitHub (pinned to 9f775e8a12)