influxdata/influxdb · error · syn::Error

catalog records must be structs with named fields

Error message

catalog records must be structs with named fields

What it means

A compile-time error from the `#[catalog_record]` proc-macro. The macro only supports structs whose fields are all named (`struct Foo { a: u64 }`), because it reads field types in order to compute the persisted-shape fingerprint and generates code that accesses fields by name. Tuple structs, unit structs, and enum inputs are rejected.

Solutions

  1. Convert the struct to use named fields (e.g. `struct Foo(u64)` becomes `struct Foo { field0: u64 }`).
  2. If a newtype is desired, wrap it inside a named-field record struct instead.
  3. Recompute the `shape` fingerprint after restructuring fields, since field order/types feed the fingerprint.

Example fix

// before
#[catalog_record(shape = 0x1234)]
struct Foo(u64);

// after
#[catalog_record(shape = 0x5678)]
struct Foo { id: u64 }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the record struct uses named fields before applying the macro:
// struct Foo { id: u64 }  // OK
// struct Foo(u64);        // NOT OK

Prevention

When it happens

Trigger: Applying `#[catalog_record(...)]` to a tuple struct (`struct Foo(u64)`), a unit struct (`struct Foo;`), or any non-struct item with unnamed fields. Raised in `expand` at the `let Fields::Named(ref fields) = item.fields else` branch.

Common situations: A developer models a record as a newtype wrapper or tuple struct for brevity, or ports a type from another serialization framework that allowed tuple structs.

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


AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/cf1a87319a9cb7be. Report an issue: GitHub.

Appendix: source

Thrown at influxdb3_catalog_macros/src/lib.rs:162

            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);
    if actual != args.shape {
        return Err(syn::Error::new(
            args.shape_span,
            shape_mismatch_message(&item.ident, args.shape, actual),
        ));
    }

View on GitHub (pinned to 06200ef96b)