{"record":{"id":"bdbe1b9209c0423b","repo":"PyO3/pyo3","slug":"complex-enum-has-a-non-unit-variant","errorCode":null,"errorMessage":"complex enum has a non-unit variant","messagePattern":"complex enum has a non-unit variant","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pyo3-macros-backend/src/pyclass.rs","lineNumber":676,"sourceCode":"            ident,\n            repr_type,\n            variants,\n        })\n    }\n}\n\nstruct PyClassComplexEnum<'a> {\n    ident: &'a syn::Ident,\n    variants: Vec<PyClassEnumVariant<'a>>,\n}\n\nimpl<'a> PyClassComplexEnum<'a> {\n    fn new(enum_: &'a mut syn::ItemEnum) -> syn::Result<Self> {\n        let witness = enum_\n            .variants\n            .iter()\n            .find(|variant| !matches!(variant.fields, syn::Fields::Unit))\n            .expect(\"complex enum has a non-unit variant\")\n            .ident\n            .to_owned();\n\n        let extract_variant_data =\n            |variant: &'a mut syn::Variant| -> syn::Result<PyClassEnumVariant<'a>> {\n                use syn::Fields;\n                let ident = &variant.ident;\n                let options = EnumVariantPyO3Options::take_pyo3_options(&mut variant.attrs)?;\n\n                let variant = match &variant.fields {\n                    Fields::Unit => {\n                        bail_spanned!(variant.span() => format!(\n                            \"Unit variant `{ident}` is not yet supported in a complex enum\\n\\\n                            = help: change to an empty tuple variant instead: `{ident}()`\\n\\\n                            = note: the enum is complex because of non-unit variant `{witness}`\",\n                            ident=ident, witness=witness))\n                    }\n                    Fields::Named(fields) => {","sourceCodeStart":658,"sourceCodeEnd":694,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/pyo3-macros-backend/src/pyclass.rs#L658-L694","documentation":"PyClassComplexEnum::new is only called after the enum has been determined to be a 'complex' enum (one containing at least one non-unit variant). It uses find + expect to grab the first non-unit variant as a witness, so the panic fires if this constructor is invoked on an enum whose variants are all unit variants — a caller-side invariant violation.","triggerScenarios":"Internal misuse: calling PyClassComplexEnum::new on an enum with only unit variants (the simple-enum path should have been taken instead).","commonSituations":"Only when forking/patching pyo3-macros-backend or routing ASTs to the wrong enum representation (e.g. after changing how #[pyclass(enum)] discriminates simple vs complex enums); invisible to regular users.","solutions":["Route unit-only enums to the simple-enum constructor instead of PyClassComplexEnum::new.","Check the caller's classification logic (e.g. the variants.any(!unit) check) before choosing the complex path.","Report to pyo3 if stock #[pyclass] triggers it on a mixed enum."],"exampleFix":"// before (caller dispatch)\nPyClassComplexEnum::new(&mut item_enum) // for enum { A, B }\n// after\nif has_non_unit_variant { PyClassComplexEnum::new(...) } else { PyClassSimpleEnum::new(...) }","handlingStrategy":"validation","validationCode":"// Dispatch correctly: only complex enums go to PyClassComplexEnum\nlet is_complex = item_enum.variants.iter().any(|v| !matches!(v.fields, syn::Fields::Unit));","typeGuard":"fn is_complex_enum(e: &syn::ItemEnum) -> bool {\n    e.variants.iter().any(|v| !matches!(v.fields, syn::Fields::Unit))\n}","tryCatchPattern":null,"preventionTips":["Check enum shape before choosing the simple vs complex codegen path.","Keep dispatch logic and constructors' expectations together in one place.","Add debug_asserts mirroring the constructor invariants."],"tags":["pyo3","proc-macro","enum","internal-invariant"],"backgroundTag":"internal-invariant-panic","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}