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

`MultipartForm` can only be derived for a struct with named…

Error message

`MultipartForm` can only be derived for a struct with named fields

What it means

This is a compile-time error from the `#[derive(MultipartForm)]` proc macro. The derive only accepts structs with named fields because it needs field identifiers to generate per-field handling code (limits, state extraction, etc.). Tuple structs (`struct Foo(String)`) and unit structs (`struct Foo;`) are rejected because the macro cannot map multipart field names to unnamed positions.

Solutions

  1. Change the struct to use named fields: `struct Upload { file: Bytes }` instead of `struct Upload(Bytes);`
  2. If you need a single-field wrapper, still give it a name: `struct Upload { data: Bytes }`
  3. Remove `#[derive(MultipartForm)]` if the type is not actually used as a multipart form extractor

Example fix

// before
#[derive(MultipartForm)]
struct Upload(Bytes);

// after
#[derive(MultipartForm)]
struct Upload {
    data: Bytes,
}
Defensive patterns

Strategy: type-guard

Type guard

// No runtime guard needed; this is a compile-time error.
// Ensure the struct uses named fields:
// pub struct MyForm {
//     field1: Bytes,
//     field2: String,
// }
// Verify with: cargo check

Prevention

When it happens

Trigger: Annotating a tuple struct (e.g., `#[derive(MultipartForm)] struct Upload(Bytes);`) or a unit struct (`struct Empty;`) with `#[derive(MultipartForm)]`. The macro matches `data_struct.fields` against `syn::Fields::Named` at line 73-81 and returns this error for `Unnamed` or `Unit`.

Common situations: Developers new to actix-multipart who try to derive on a tuple struct, or who copy a serde pattern using unnamed fields. Refactoring a named-field struct into a tuple struct without removing the derive.

Related errors


AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09). Data as JSON: /api/errors/73b0dcfae6a23a9c. Report an issue: GitHub.

Appendix: source

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

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,
        Err(err) => return err.write_errors().into(),
    };

    // Parse the field attributes
    let parsed = match fields
        .named
        .iter()
        .map(|field| {
            let rust_name = field.ident.as_ref().unwrap();
            let attrs = FieldAttrs::from_field(field).map_err(|err| err.write_errors())?;

View on GitHub (pinned to 4d435abc28)