actix/actix-web · error · syn::Error

`MultipartForm` can only be derived for structs

Error message

`MultipartForm` can only be derived for structs

What it means

The `#[derive(MultipartForm)]` proc macro only accepts plain structs. At actix-multipart-derive/src/lib.rs:66 the macro matches `syn::Data::Struct` and rejects enums and unions outright, because the generated `MultipartCollect` impl constructs `Self { ... }` field-by-field, which is only valid syntax for structs. Anything else would produce nonsensical code, so the macro fails fast.

Source

Thrown at actix-multipart-derive/src/lib.rs:66

    ty: &'t Type,
}

/// Implements `MultipartCollect` for a struct so that it can be used with the `MultipartForm`
/// extractor.
///
/// See [`actix_multipart::form::MultipartForm`] for supported fields and attributes.
///
/// [`actix_multipart::form::MultipartForm`]: https://docs.rs/actix-multipart/latest/actix_multipart/form/struct.MultipartForm.html
#[proc_macro_derive(MultipartForm, attributes(multipart))]
pub fn impl_multipart_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input: syn::DeriveInput = parse_macro_input!(input);

    let name = &input.ident;

    let data_struct = match &input.data {
        syn::Data::Struct(data_struct) => data_struct,
        _ => {
            return compile_err(syn::Error::new(
                input.ident.span(),
                "`MultipartForm` can only be derived for structs",
            ))
        }
    };

    let fields = match &data_struct.fields {
        syn::Fields::Named(fields_named) => fields_named,
        _ => {
            return compile_err(syn::Error::new(
                input.ident.span(),
                "`MultipartForm` can only be derived for a struct with named fields",
            ))
        }
    };

    let attrs = match MultipartFormAttrs::from_derive_input(&input) {
        Ok(attrs) => attrs,

View on GitHub (pinned to 937960ca67)

Solutions

  1. Change the target type from an enum/union to a struct with named fields.
  2. If you need multiple payload shapes, wrap them: use one struct per shape and pick the struct in the handler rather than a single enum.
  3. Remove the `#[derive(MultipartForm)]` attribute if this type is not actually a multipart form.

Example fix

// before
#[derive(MultipartForm)]
enum Form {
    A { name: String },
    B { file: Field },
}

// after
#[derive(MultipartForm)]
struct FormA { name: String }

#[derive(MultipartForm)]
struct FormB { file: Field }
Defensive patterns

Strategy: validation

Validate before calling

// Before adding the derive, confirm the item is a struct with named fields.
// There is no runtime API to guard; this is a compile-time check. Verify:
//   - the type is declared with `struct Name { ... }`
//   - it is not an `enum` or `union`
// If unsure, the compiler/macro will flag it; fix the type declaration.

Prevention

When it happens

Trigger: Annotating an `enum` or `union` (e.g. `#[derive(MultipartForm)] enum Form {...}`) with the derive. Also happens if a type alias or macro-generated item that is not a struct carries the attribute.

Common situations: Modeling a multipart payload as an enum of variants (a natural design for "one of several shapes"), or copy-pasting a serde enum that was previously serialized and trying to reuse it as a multipart form type.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/3690a299ad529506.json. Report an issue: GitHub.