swc-project/swc · error
enum member types must be consistent: {:?}
Error message
enum member types must be consistent: {:?} What it means
Decode's derive folds all non-unknown variants into one EnumType (Unit/One/Struct) and requires the mix to stay uniform. Named-field variants cannot coexist with single-field tuple ('newtype') variants — the (Struct, One) combination falls into the catch-all and panics; note that Unit+One is internally upgraded to Struct, so Unit+One+One also ends in this panic. The {:?} payload prints the conflicting pair (e.g. (Struct, One)) so you can see which shapes collided.
Source
Thrown at crates/ast_node/src/encoding/decode.rs:99
None,
|mut sum, next| {
let ty = match &next.fields {
syn::Fields::Named(_) => EnumType::Struct,
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => EnumType::One,
syn::Fields::Unit => EnumType::Unit,
syn::Fields::Unnamed(_) => {
panic!("more than 1 unnamed member field are not allowed")
}
};
match (*sum.get_or_insert(ty), ty) {
(EnumType::Struct, EnumType::Struct)
| (EnumType::Struct, EnumType::Unit)
| (EnumType::Unit, EnumType::Unit)
| (EnumType::One, EnumType::One) => (),
(EnumType::Unit, EnumType::One)
| (EnumType::One, EnumType::Unit)
| (_, EnumType::Struct) => sum = Some(EnumType::Struct),
_ => panic!("enum member types must be consistent: {:?}", (sum, ty)),
}
sum
},
);
let enum_type = enum_type.expect("enum cannot be empty");
let mut iter = data.variants.iter().peekable();
let unknown_arm: Option<syn::Arm> = iter.next_if(|variant| is_unknown(&variant.attrs))
.map(|unknown| {
let name = &unknown.ident;
assert!(
unknown.discriminant.is_none(),
"unknown member is not allowed custom discriminant"
);
assert!(
is_with(&unknown.attrs).is_none(),
"unknown member is not allowed with type"
);View on GitHub (pinned to 5176682b65)
Solutions
- Make all variants the same shape — usually convert newtype variants `B(u32)` to named ones `B { value: u32 }`.
- Alternatively keep newtypes but wrap named payloads in a struct so every variant is single-field.
- Read the (X, Y) pair in the panic message to find which two variant shapes disagree.
Example fix
// before
#[derive(Encode, Decode)]
enum Value {
Num(f64), // One
Obj { props: Vec<Prop> }, // Struct — inconsistent
}
// after — uniform named-struct variants
#[derive(Encode, Decode)]
enum Value {
Num { value: f64 },
Obj { props: Vec<Prop> },
} Defensive patterns
Strategy: validation
Prevention
- Pick one variant style per enum (all named, or all single-field newtype) and enforce it in review.
- Treat the panic's (X, Y) EnumType pair as a pointer to the two disagreeing variants.
- cargo check after every variant addition; shape panics surface instantly.
When it happens
Trigger: An enum mixing `A { x: u32 }` (Struct) with `B(u32)` (One) under derive(Decode)/#[ast_node]; or an enum with Unit plus multiple One variants, which folds to Struct and then clashes with a later One.
Common situations: Growing an AST enum incrementally — starting with newtype variants and later adding a named-variant node; merging vendor branches whose variants use different styles.
Related errors
- more than 1 unnamed member field are not allowed
- Cannot use both #[encoding(with)] and #[encoding(ignore)] at
- unknown member must be a tag and a value
- named enum unsupported
- unsupported discriminant type
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/75a37544dd01c5f6.
Report an issue: GitHub.