dbt-labs/dbt-core · error · syn::Error
Unsupported enum field type shape; expected i32, Option<i32>
Error message
Unsupported enum field type shape; expected i32, Option<i32>, or Vec<i32>
What it means
When a prost field carries `#[prost(enumeration = "...")]`, the ProtoNew derive replaces the field's i32-based type with the enum type in the generated `new()` signature. It only knows three shapes: `i32`, `Option<i32>`, and `Vec<i32>` (or prost's `::prost::alloc::vec::Vec<i32>`); any other type shape for an enumeration field fails at compile time.
Source
Thrown at crates/proto-rust-macros/src/lib.rs:217
// Option<i32>
if let Some(inner) = extract_generic(field_ty, "Option")
&& is_i32(inner)
{
let param_ty = quote! { ::core::option::Option<#enum_path> };
let init = quote! { #param_ident.map(|v| v as i32) };
return Ok((param_ty, init));
}
// Vec<i32> and ::prost::alloc::vec::Vec<i32>
if is_vec_of_i32(field_ty) {
let vec_path = vec_path_for(field_ty);
let param_ty = quote! { #vec_path<#enum_path> };
let init = quote! { #param_ident.into_iter().map(|v| v as i32).collect() };
return Ok((param_ty, init));
}
Err(syn::Error::new(
field_ty.span(),
"Unsupported enum field type shape; expected i32, Option<i32>, or Vec<i32>",
))
}
fn is_i32(ty: &Type) -> bool {
match ty {
Type::Path(tp) => {
if let Some(seg) = tp.path.segments.last() {
seg.ident == "i32" && matches!(seg.arguments, PathArguments::None)
} else {
false
}
}
_ => false,
}
}
View on GitHub (pinned to 0267ce9170)
Solutions
- Change the field type to one of the supported shapes: `i32`, `Option<i32>`, or `Vec<i32>` (prost's default shapes for optional/repeated enumeration fields).
- Change the generated `new()` parameter to accept the enum type manually: drop ProtoNew for that struct and hand-write the constructor.
- Extend `map_enum_param_and_init` in crates/proto-rust-macros/src/lib.rs to recognize the additional shape (e.g. unwrap type aliases or new wrappers) before matching.
Example fix
// before
#[derive(ProtoNew)]
pub struct Msg {
#[prost(enumeration = "Kind")]
pub kind: Option<Option<i32>>,
}
// after
#[derive(ProtoNew)]
pub struct Msg {
#[prost(enumeration = "Kind")]
pub kind: Option<i32>,
} Defensive patterns
Strategy: type-guard
Validate before calling
// before deriving, confirm enum fields use one of:
// i32 | Option<i32> | Vec<i32> | ::prost::alloc::vec::Vec<i32>
fn is_supported_enum_shape(ty: &str) -> bool {
matches!(ty, "i32" | "Option<i32>" | "Vec<i32>" | "::prost::alloc::vec::Vec<i32>")
} Type guard
fn is_i32(ty: &syn::Type) -> bool {
matches!(ty, syn::Type::Path(tp)
if tp.path.segments.last().map_or(false, |s| s.ident == "i32"))
} Prevention
- Keep prost enumeration fields in their default shapes (i32/Option<i32>/Vec<i32>)
- Avoid type aliases on enumeration field types (aliases break ident matching)
- Test the generated `new()` in a compile-fail/doctest when adding new enum fields
When it happens
Trigger: Deriving ProtoNew on a struct whose `#[prost(enumeration="X")]` field is typed as e.g. `Box<i32>`, `HashMap<..>`, `Option<Vec<i32>>`, a type alias to i32, or a fully-qualified path the shape matchers (`is_i32`, `extract_generic`, `is_vec_of_i32`) do not recognize.
Common situations: prost-generated code where the enumeration is wrapped in additional containers (repeated+optional combos, boxed fields); hand-written structs using type aliases like `type Int32 = i32;` (alias names break the `i32` ident check); upgrading prost and getting a different Vec path spelling.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Unnamed field not supported
- ProtoEnumSerde can only be derived for enums
- Failed to parse value of `{field}` as path.
- ProtoNew only supports structs with named fields
- ProtoNew can only be derived for structs
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/0cade84803c8e03d.
Report an issue: GitHub.