swc-project/swc · error · TypeError
A class descriptor's .kind property must be "class", but a d
Error message
A class descriptor's .kind property must be "class", but a decorator created a class descriptor with .kind "${kind}" What it means
While expanding #[derive(DeserializeEnum)] (used by swc's #[ast_node] enums), the macro reads each variant's #[tag(..)] attribute and parses its token stream with syn::parse2::<VariantAttr>().expect("failed to parse #[tag] attribute"). VariantAttr only accepts a comma-separated list of literals (Punctuated<Lit, Comma>), so any non-literal token inside #[tag(..)] makes parse2 fail and panics the proc macro at compile time.
Source
Thrown at crates/swc_ecma_transforms_base/src/helpers/generated/_decorate.rs:239
}
function _toElementFinisherExtras(elementObject) {
var element = _toElementDescriptor(elementObject);
var finisher = _optionalCallableProperty(elementObject, "finisher");
var extras = _toElementDescriptors(elementObject.extras);
return { element: element, finisher: finisher, extras: extras };
}
function _fromClassDescriptor(elements) {
var obj = { kind: "class", elements: elements.map(_fromElementDescriptor) };
var desc = { value: "Descriptor", configurable: true };
Object.defineProperty(obj, Symbol.toStringTag, desc);
return obj;
}
function _toClassDescriptor(obj) {
var kind = String(obj.kind);
if (kind !== "class") {
throw new TypeError("A class descriptor's .kind property must be \"class\", but a decorator" + " created a class descriptor with .kind \"" + kind + "\"");
}
_disallowProperty(obj, "key", "A class descriptor");
_disallowProperty(obj, "placement", "A class descriptor");
_disallowProperty(obj, "descriptor", "A class descriptor");
_disallowProperty(obj, "initializer", "A class descriptor");
_disallowProperty(obj, "extras", "A class descriptor");
var finisher = _optionalCallableProperty(obj, "finisher");
var elements = _toElementDescriptors(obj.elements);
return { elements: elements, finisher: finisher };
}
function _disallowProperty(obj, name, objectType) {
if (obj[name] !== undefined) throw new TypeError(objectType + " can't have a ." + name + " property.");
}
function _optionalCallableProperty(obj, name) {
var value = obj[name];
if (value !== undefined && typeof value !== "function") {
throw new TypeError("Expected '" + name + "' to be a function");View on GitHub (pinned to 5176682b65)
Solutions
- Write the attribute as a comma-separated list of string literals: #[tag("TagName")] or #[tag("A", "B")].
- Quote every tag - identifiers, numbers-as-names, and constants are not accepted, only literals.
- Separate multiple tags with commas; a single string "*" is the reserved wildcard.
- Keep the outer form as a list (#[tag(..)]), never #[tag = ..] or #[tag(..) = ..].
Example fix
// before: bare identifier -> panic 'failed to parse #[tag] attribute'
#[ast_node("Meta")]
enum Node {
#[tag(Ident)]
Ident(Box<Ident>),
}
// after: quoted string literal
#[ast_node("Meta")]
enum Node {
#[tag("Ident")]
Ident(Box<Ident>),
} Defensive patterns
Strategy: validation
Validate before calling
// Authoring-time pattern: tags are comma-separated string literals only.
// OK: #[tag("Ident")]
// OK: #[tag("A", "B")]
// OK: #[tag("*")] (wildcard)
// BAD: #[tag(Ident)] #[tag("a" "b")] #[tag = "x"]
// If you generate enums, validate before emitting:
fn valid_tag(tag: &str) -> bool {
tag.chars().all(|c| !c.is_whitespace()) && !tag.is_empty()
}
let attr = format!("#[tag({})]", tags.iter().map(|t| format!("{:?}", t)).collect::<Vec<_>>().join(", ")); Prevention
- Always quote tag names as string literals; identifiers are rejected by syn::parse2.
- Separate multiple tags with commas.
- Copy tag examples from existing swc AST crates (e.g. swc_ecma_ast) rather than serde docs.
When it happens
Trigger: Writing #[tag(Name)] (a bare identifier instead of the string literal "Name"), #[tag("a" "b")] without a comma, function-call syntax like #[tag(tag("x"))], or expressions inside the attribute. Note the separate earlier panic for the wrong meta form (#[tag = "x"] gives '#[tag] attribute must be in form of #[tag(..)]'), and the later assert requiring at least one tag per variant.
Common situations: First-time authors of swc AST node enums copying serde's #[serde(tag = "type")] habits; renaming tags with unquoted identifiers; typos like missing quotes after refactoring; version changes that moved from one accepted syntax to strictly literal lists.
Related errors
- ${objectType} can't have a .${name} property.
- An element descriptor's .kind property must be either "metho
- An element descriptor's .placement property must be one of "
- Expected '${name}' to be a function
- Finishers must return a constructor.
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/ef53e8779575f162.
Report an issue: GitHub.