{"record":{"id":"d13eab88ad4b6783","repo":"influxdata/influxdb","slug":"catalog-records-cannot-be-generic-the-persisted-bytes-must","errorCode":null,"errorMessage":"catalog records cannot be generic — the persisted bytes must be one fixed shape","messagePattern":"catalog records cannot be generic — the persisted bytes must be one fixed shape","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"influxdb3_catalog_macros/src/lib.rs","lineNumber":155,"sourceCode":"                proc_macro2::Span::call_site(),\n                \"missing `shape = 0x…` — build with a placeholder to be told the expected value\",\n            )\n        })?;\n\n        Ok(Self {\n            id,\n            shape: shape.base10_parse()?,\n            shape_span: shape.span(),\n            flags,\n        })\n    }\n}\n\nfn expand(metas: Punctuated<Meta, Token![,]>, item: ItemStruct) -> syn::Result<TokenStream2> {\n    let args = Args::parse(metas)?;\n\n    if !item.generics.params.is_empty() {\n        return Err(syn::Error::new(\n            item.generics.span(),\n            \"catalog records cannot be generic — the persisted bytes must be one fixed shape\",\n        ));\n    }\n\n    let Fields::Named(ref fields) = item.fields else {\n        return Err(syn::Error::new(\n            item.fields.span(),\n            \"catalog records must be structs with named fields\",\n        ));\n    };\n\n    let field_types: Vec<&Type> = fields.named.iter().map(|f| &f.ty).collect();\n    for ty in &field_types {\n        types::check(ty)?;\n    }\n\n    let actual = shape::fingerprint(&field_types);","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_catalog_macros/src/lib.rs#L137-L173","documentation":"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.","triggerScenarios":"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`.","commonSituations":"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.","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."],"exampleFix":"// before\n#[catalog_record(shape = 0x1234)]\nstruct NodeRecord<T> { id: T }\n\n// after\n#[catalog_record(shape = 0x1234)]\nstruct NodeRecord { id: u64 }","handlingStrategy":"type-guard","validationCode":"// Compile-time: assert the struct is non-generic before annotating\n// macro_rules guard not possible for generics; instead ensure:\nstruct MyRecord { /* concrete fields only */ }\nconst _: () = { let _ = core::mem::size_of::<MyRecord>; };","typeGuard":"fn is_non_generic<T>() -> bool { true } // if it compiles with concrete args, the record type has no free generics","tryCatchPattern":null,"preventionTips":["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."],"tags":["proc-macro","compile-time","generics","serialization"],"backgroundTag":"invalid-argument-value","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}