bevyengine/bevy · error · syn::Error
#[{meta}] only supports structs, not unions
Error message
#[{meta}] only supports structs, not unions What it means
Companion of the enum case: get_struct_fields rejects unions when a struct-only Bevy derive macro is applied to a `union` item. The error span points at the `union` keyword. Bevy derives need per-field access to generate code (component storage, param fetching), which unions cannot provide safely.
Source
Thrown at crates/bevy_macro_utils/src/shape.rs:17
use syn::{
punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataEnum, DataUnion, Error,
Field, Fields,
};
/// Get the fields of a data structure if that structure is a struct;
/// otherwise, return a compile error that points to the site of the macro invocation.
///
/// `meta` should be the name of the macro calling this function.
pub fn get_struct_fields<'a>(data: &'a Data, meta: &str) -> Result<&'a Fields, Error> {
match data {
Data::Struct(data_struct) => Ok(&data_struct.fields),
Data::Enum(DataEnum { enum_token, .. }) => Err(Error::new(
enum_token.span(),
format!("#[{meta}] only supports structs, not enums"),
)),
Data::Union(DataUnion { union_token, .. }) => Err(Error::new(
union_token.span(),
format!("#[{meta}] only supports structs, not unions"),
)),
}
}
/// Return an error if `Fields` is not `Fields::Named`
pub fn require_named<'a>(fields: &'a Fields) -> Result<&'a Punctuated<Field, Comma>, Error> {
if let Fields::Named(fields) = fields {
Ok(&fields.named)
} else {
Err(Error::new(
fields.span(),
"Unnamed fields are not supported here",
))
}
}
View on GitHub (pinned to 396ca72708)
Solutions
- Move the derive off the union onto a plain struct wrapper.
- Replace the union with a struct for ECS data; keep unions in FFI boundary code only.
- Delete the stale derive if it was attached accidentally.
Example fix
// before
#[derive(Bundle)]
union RawInput {
bytes: [u8; 8],
fields: (u32, u32),
}
// after
#[derive(Bundle)]
struct InputBundle {
raw: RawInput, // union kept as a plain field, no derive on it
} Defensive patterns
Strategy: validation
Prevention
- Never attach Bevy derives to `union` items; wrap unions in a struct field instead.
- Keep FFI union declarations derive-free and in a separate module from ECS types.
- When copy-pasting derives, check the item kind (`struct` vs `enum` vs `union`) matches what the macro supports.
When it happens
Trigger: Applying #[derive(Bundle)], #[derive(SystemParam)], #[derive(QueryData)], #[derive(WorldQuery)], or the render Specializer derive to a Rust `union` item.
Common situations: FFI or zero-copy code where a union was declared in the same module as Bevy types and a derive was attached by copy-paste or IDE auto-complete.
Related errors
- Union types are not supported yet.
- #[{meta}] only supports structs, not enums
- Unnamed fields are not supported here
- Expected a Template type path
- Can only derive VariantDefaults for enums
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/2c371088e3e1da4f.
Report an issue: GitHub.