siyuan-note/siyuan · error
endpoint does not accept multipart data
Error message
endpoint does not accept multipart data
What it means
DecodeMultipart is the generic multipart/form-data decoder for contract-bound endpoints. It first checks that the endpoint's declared body kind is MultipartBody or FormBody; if the endpoint was defined with another body type (e.g. JSON), multipart data is rejected. This prevents silently feeding form data to endpoints that expect a different content type.
Solutions
- Send the request as JSON (Content-Type: application/json) matching the endpoint's declared body type.
- Use a different endpoint that is declared with MultipartBody or FormBody for file/form uploads.
- If you own the endpoint, update its contract definition to MultipartBody/FormBody (and run the API contract generation/check steps).
Example fix
// before
fetchPost("/api/endpoint", formData) // endpoint expects JSON
// after
fetchPost("/api/endpoint", {field: value}) // or use the declared multipart endpoint Defensive patterns
Strategy: validation
Validate before calling
function checkBodyKind(endpointDef, request) {
const isForm = request instanceof FormData;
const multipartOk = endpointDef.body === "multipart" || endpointDef.body === "form";
if (isForm && !multipartOk) throw new Error("endpoint " + endpointDef.name + " does not accept FormData");
} Type guard
const isFormData = (b) => typeof FormData !== "undefined" && b instanceof FormData;
Try / catch
try { await api.post(url, body); } catch (e) { if (String(e).includes("does not accept multipart")) { await api.post(url, await formDataToJson(body)); } else { throw e; } } Prevention
- Match the request encoding to the endpoint's declared body type.
- Never set Content-Type manually when switching between JSON and FormData.
- Consult the generated API contract to confirm which endpoints take multipart.
When it happens
Trigger: POSTing multipart/form-data to an endpoint whose contract definition declares a non-multipart, non-form body (e.g. JSONBody).
Common situations: A client sends FormData with fetch/axios to an API that expects a JSON body; the endpoint contract was changed or bound incorrectly; a plugin calls a JSON-only route with form encoding.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- asset path [ ] does not match data path [ ]
- box and dataPath cannot be used together
- dom is required
- multipart form is missing
- open form part [ ] file [ ] error
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/4804dc0781b21d26.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/multipart.go:40
if !field.IsExported() || field.Anonymous || field.Tag.Get("json") == "" {
return fmt.Errorf("multipart fields must be named and exported: %s", field.Name)
}
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 {View on GitHub (pinned to 9f775e8a12)