PyO3/pyo3 · error

named field has an identifier

Error message

named field has an identifier

What it means

When extracting named fields of a #[pyclass] enum variant, the macro unwraps each field's identifier with expect. As with struct fields, syn guarantees named fields carry identifiers, so this panic signals a corrupted AST or wrong code path (e.g. tuple/unit variants reaching the named-field branch).

Source

Thrown at pyo3-macros-backend/src/pyclass.rs:699

            |variant: &'a mut syn::Variant| -> syn::Result<PyClassEnumVariant<'a>> {
                use syn::Fields;
                let ident = &variant.ident;
                let options = EnumVariantPyO3Options::take_pyo3_options(&mut variant.attrs)?;

                let variant = match &variant.fields {
                    Fields::Unit => {
                        bail_spanned!(variant.span() => format!(
                            "Unit variant `{ident}` is not yet supported in a complex enum\n\
                            = help: change to an empty tuple variant instead: `{ident}()`\n\
                            = note: the enum is complex because of non-unit variant `{witness}`",
                            ident=ident, witness=witness))
                    }
                    Fields::Named(fields) => {
                        let fields = fields
                            .named
                            .iter()
                            .map(|field| PyClassEnumVariantNamedField {
                                ident: field.ident.as_ref().expect("named field has an identifier"),
                                ty: &field.ty,
                                attrs: &field.attrs,
                                span: field.span(),
                            })
                            .collect();

                        PyClassEnumVariant::Struct(PyClassEnumStructVariant {
                            ident,
                            fields,
                            options,
                            attrs: variant.attrs.clone(),
                        })
                    }
                    Fields::Unnamed(types) => {
                        let fields = types
                            .unnamed
                            .iter()
                            .map(|field| PyClassEnumVariantUnnamedField {

View on GitHub (pinned to ac9b6899d3)

Solutions

  1. Use an unmodified pyo3/syn combination from the same supported versions.
  2. Ensure any intervening macro passes preserve field identifiers on named fields.
  3. Route non-named variants to their proper match arms before this code runs.
  4. Report a minimal repro to pyo3 if valid code panics.

Example fix

// before (variant reaching the wrong branch)
enum E { Tuple(u32) } // processed as named-fields
// after
enum E { Struct { value: u32 } } // matches Fields::Named branch
Defensive patterns

Strategy: validation

Validate before calling

// Only route Fields::Named variants through the named-field extractor
matches!(variant.fields, syn::Fields::Named(_));

Type guard

fn has_named_fields(v: &syn::Variant) -> bool {
    matches!(v.fields, syn::Fields::Named(_))
}

Prevention

When it happens

Trigger: Internal path violation: a variant with tuple or unit fields processed by the Fields::Named branch; not triggerable from valid user code with #[pyclass] enums.

Common situations: Patched macro backends, syn version mismatches, or third-party attribute macros that rewrite variant fields before pyo3 sees them.

Related errors


AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05). Data as JSON: /api/errors/788b874fc063e93c. Report an issue: GitHub.