swc-project/swc · error
unknown member must be a tag and a value
Error message
unknown member must be a tag and a value
What it means
The #[encoding(unknown)] variant is swc's forward-compatibility escape hatch: unknown CBOR tags on the wire decode into it. The macro only generates code for two shapes — one unnamed field (bare tag: Unknown(u32)) and two unnamed fields (tag + value: Unknown(u32, T), where it decodes fields.unnamed[1]). Any other arity (zero or three-plus unnamed fields) cannot be mapped, so expansion panics with 'unknown member must be a tag and a value'.
Source
Thrown at crates/ast_node/src/encoding/decode.rs:140
assert_eq!(enum_type, EnumType::Unit);
syn::parse_quote! {
tag => #ident::#name(tag),
}
}
2 => {
assert_eq!(enum_type, EnumType::One);
let val_ty = &fields.unnamed[1].ty;
syn::parse_quote! {
tag => {
let tag: u32 = tag.try_into().map_err(|_| cbor4ii::core::error::DecodeError::CastOverflow {
name: &"unknown-tag",
})?;
let val = <#val_ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?;
#ident::#name(tag, val)
},
}
}
_ => panic!("unknown member must be a tag and a value"),
},
_ => panic!("named enum unsupported"),
}
});
if matches!(enum_type, EnumType::Struct) {
assert!(
unknown_arm.is_none(),
"struct enum does not allow unknown variants"
);
}
let mut discriminant: u32 = 0;
let fields = iter
.map(|field| -> syn::Arm {
match field.discriminant.as_ref() {
Some((_, syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(lit), .. }))) => {
discriminant = lit.base10_parse::<u32>().unwrap();View on GitHub (pinned to 5176682b65)
Solutions
- Declare the unknown variant with exactly `#[encoding(unknown)] Unknown(u32)` for all-unit enums, or `#[encoding(unknown)] Unknown(u32, YourUnknownType)` for payload-carrying enums.
- Prefer #[ast_node(...)] over manual derives so the Unknown variant is generated for you.
- Place the unknown variant first — a later assert also requires it to precede tagged variants.
Example fix
// before
#[derive(Encode, Decode)]
enum Expr {
#[encoding(unknown)]
Unknown,
Num { value: f64 },
}
// after — tag + value tuple
#[derive(Encode, Decode)]
enum Expr {
#[encoding(unknown)]
Unknown(u32, swc_common::unknown::Unknown),
Num { value: f64 },
} Defensive patterns
Strategy: validation
Prevention
- Prefer #[ast_node(...)] over manual Encode/Decode derives so the Unknown variant is generated with the right arity.
- If hand-writing, copy the exact shapes: Unknown(u32) for unit enums, Unknown(u32, swc_common::unknown::Unknown) otherwise.
- Keep the #[encoding(unknown)] variant first in the enum.
When it happens
Trigger: Hand-writing derive(Encode/Decode) (not via #[ast_node]) on an enum and annotating a variant #[encoding(unknown)] whose shape is `Unknown`, `Unknown(u32, u32, u32)`, etc.
Common situations: Custom AST nodes for swc plugins or swc_ast_unknown builds; hand-porting the auto-generated Unknown variant and getting its arity wrong. #[ast_node] injects the correct Unknown(u32) / Unknown(u32, swc_common::unknown::Unknown) shapes itself, so this fires only for manual derives.
Related errors
- named enum unsupported
- more than 1 unnamed member field are not allowed
- enum member types must be consistent: {:?}
- unsupported discriminant type
- unknown member must be a tag and a value
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/0a6d35ed1035359a.
Report an issue: GitHub.