influxdata/influxdb · error · syn::Error

⚠ Field shape of catalog record

Error message

⚠ Field shape of catalog record ({ident}) changed ⚠

Declared shape:

    {declared:#010x}

Actual shape:

    {actual:#010x}

The fingerprint covers the field types, in order — adding, removing, reordering, or retyping a field moves it. Field renames do not, because bitcode does not encode field names.

Once a catalog record type has been shipped in a released version of the software, it cannot be modified. To introduce new functionality, add a new record type.

If this is the first time you're adding this record type, or are making modifications to it prior to releasing it, update the `shape` argument to the actual value above.

What it means

A compile-time guard from the `#[catalog_record]` macro. The macro computes a fingerprint (via `shape::fingerprint`) over the record's field types in order and compares it with the `shape = 0x...` argument. A mismatch means the field layout (added/removed/reordered/retyped fields — renames don't count, bitcode doesn't encode names) differs from the declared persisted shape, which would silently corrupt previously written catalog bytes.

Solutions

  1. If this is the first release of the record type (or changes are pre-release), update the `shape` argument to the actual fingerprint printed in the error message.
  2. If the record type shipped in a released version, revert the field change; never modify it.
  3. To add new functionality, define a NEW record type with its own shape instead of editing the existing one.

Example fix

// before (added a field but kept old shape)
#[catalog_record(shape = 0x12345678)]
struct Foo { id: u64, name: String }

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

Strategy: validation

Validate before calling

// Before changing a shipped record's fields, check compatibility:
// 1. Only append via a NEW record type if the old one was released.
// 2. If pre-release, copy the 'Actual shape' hex from the error into `shape = 0x...`.

Prevention

When it happens

Trigger: Modifying the fields of a struct annotated with `#[catalog_record(shape = X)]` (adding, removing, reordering, or changing a field's type) without updating the `shape` argument. Raised in `expand` when `actual != args.shape`.

Common situations: A developer adds a new field to an already-released catalog record, reorders fields for tidiness, or changes `u32` to `u64` — all during a routine refactor of shipped code.

Related errors


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

Appendix: source

Thrown at influxdb3_catalog_macros/src/lib.rs:175

            "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),
        ));
    }

    let ident = &item.ident;
    let id = &args.id;
    let flags = args
        .flags
        .map(|f| quote!(#f))
        .unwrap_or_else(|| quote!(crate::format::RecordFlags::none()));

    Ok(quote! {
        #[derive(
            Debug,
            Clone,
            PartialEq,
            Eq,

View on GitHub (pinned to 06200ef96b)