influxdata/influxdb · error · syn::Error
catalog records cannot be generic — the persisted bytes…
Error message
catalog records cannot be generic — the persisted bytes must be one fixed shape
What it means
This is a compile-time error from the influxdb3_catalog_macros proc-macro (`#[catalog_record]`). Catalog record structs are serialized to bytes via bitcode with a single fixed wire shape, so the macro rejects any struct declared with generic parameters (`struct Foo<T>`) because generics would produce multiple possible persisted shapes. The check happens in `expand` before any fingerprinting.
Solutions
- Remove the generic parameters from the struct and use concrete types for all fields.
- If several concrete variants are needed, declare one non-generic record struct per variant.
- If type abstraction is required, store a wire type declared in `records::types` and convert to the runtime type in the `apply` method.
Example fix
// before
#[catalog_record(shape = 0x1234)]
struct NodeRecord<T> { id: T }
// after
#[catalog_record(shape = 0x1234)]
struct NodeRecord { id: u64 } Defensive patterns
Strategy: type-guard
Validate before calling
// Compile-time: assert the struct is non-generic before annotating
// macro_rules guard not possible for generics; instead ensure:
struct MyRecord { /* concrete fields only */ }
const _: () = { let _ = core::mem::size_of::<MyRecord>; }; Type guard
fn is_non_generic<T>() -> bool { true } // if it compiles with concrete args, the record type has no free generics Prevention
- Never introduce type parameters into #[catalog_record] structs.
- Define one concrete record type per persisted shape.
- Use wire types from records::types plus conversion in apply for abstraction.
When it happens
Trigger: Applying the `#[catalog_record(...)]` attribute macro to a struct that declares generic parameters, e.g. `#[catalog_record(shape = 0x...)] struct MyRecord<T> { field: T }`. Raised during compilation by `expand`, invoked from `catalog_record`.
Common situations: A developer tries to make a catalog record reusable over several value types, or wraps an existing generic container type as a record, or adds a generic parameter while refactoring field types.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ` ` is generic. Only may take type parameters in a catalog…
- ` ` must have exactly one type parameter
- catalog records must be structs with named fields
- ⚠ Field shape of catalog record
- ` ` is a qualified path. A catalog record may only hold…
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/d13eab88ad4b6783.
Report an issue: GitHub.
Appendix: source
Thrown at influxdb3_catalog_macros/src/lib.rs:155
proc_macro2::Span::call_site(),
"missing `shape = 0x…` — build with a placeholder to be told the expected value",
)
})?;
Ok(Self {
id,
shape: shape.base10_parse()?,
shape_span: shape.span(),
flags,
})
}
}
fn expand(metas: Punctuated<Meta, Token![,]>, item: ItemStruct) -> syn::Result<TokenStream2> {
let args = Args::parse(metas)?;
if !item.generics.params.is_empty() {
return Err(syn::Error::new(
item.generics.span(),
"catalog records cannot be generic — the persisted bytes must be one fixed shape",
));
}
let Fields::Named(ref fields) = item.fields else {
return Err(syn::Error::new(
item.fields.span(),
"catalog records must be structs with named fields",
));
};
let field_types: Vec<&Type> = fields.named.iter().map(|f| &f.ty).collect();
for ty in &field_types {
types::check(ty)?;
}
let actual = shape::fingerprint(&field_types);View on GitHub (pinned to 06200ef96b)