bevyengine/bevy · error · syn::Error

conflicting type data registration

Error message

conflicting type data registration

What it means

The bevy_reflect derive stores each special registration (Clone, Debug, PartialEq, PartialOrd, Hash) as a single optional impl. While parsing `#[reflect(...)]`, the plain form (`#[reflect(Hash)]`, sets `Implemented`) and the custom-function form (`#[reflect(Hash(my_fn))]`, merges `Custom`) share that one slot; `TraitImpl::merge` (crates/bevy_reflect/derive/src/container_attributes.rs:67) returns the compile error "conflicting type data registration" when both sides already carry an implementation.

Source

Thrown at crates/bevy_reflect/derive/src/container_attributes.rs:75

    /// The trait is registered with a custom function rather than an actual implementation.
    Custom(Path, Span),
}

impl TraitImpl {
    /// Merges this [`TraitImpl`] with another.
    ///
    /// Update `self` with whichever value is not [`TraitImpl::NotImplemented`].
    /// If `other` is [`TraitImpl::NotImplemented`], then `self` is not modified.
    /// An error is returned if neither value is [`TraitImpl::NotImplemented`].
    pub fn merge(&mut self, other: TraitImpl) -> Result<(), syn::Error> {
        match (&self, other) {
            (TraitImpl::NotImplemented, value) => {
                *self = value;
                Ok(())
            }
            (_, TraitImpl::NotImplemented) => Ok(()),
            (_, TraitImpl::Implemented(span) | TraitImpl::Custom(_, span)) => {
                Err(syn::Error::new(span, CONFLICTING_TYPE_DATA_MESSAGE))
            }
        }
    }
}

/// A collection of attributes used for deriving `FromReflect`.
#[derive(Clone, Default)]
pub(crate) struct FromReflectAttrs {
    auto_derive: Option<LitBool>,
}

impl FromReflectAttrs {
    /// Returns true if `FromReflect` should be automatically derived as part of the `Reflect` derive.
    pub fn should_auto_derive(&self) -> bool {
        self.auto_derive.as_ref().is_none_or(LitBool::value)
    }
}

View on GitHub (pinned to 396ca72708)

Solutions

  1. Register each trait exactly once per type
  2. If you need a custom function, use only the custom form: replace `#[reflect(Hash)]` with `#[reflect(my_hash_fn)]`-style `#[reflect(Hash(my_hash_fn))]`, not both
  3. Consolidate everything into a single `#[reflect(...)]` list so duplicates are visually obvious

Example fix

// before: Hash registered twice -> conflicting type data registration
#[derive(Reflect)]
#[reflect(Hash, Hash(custom_hash_fn))]
struct Foo { ... }

// after: single custom registration
#[derive(Reflect)]
#[reflect(Hash(custom_hash_fn))]
struct Foo { ... }
Defensive patterns

Strategy: validation

Validate before calling

null

Prevention

When it happens

Trigger: Listing one of the specially-handled traits twice on the same type with implementations on both sides: `#[reflect(Hash, Hash(my_hash_fn))]`, `#[reflect(Debug(custom), Debug)]`, or two custom functions `#[reflect(Debug(a), Debug(b))]` — including across multiple `#[reflect(...)]` attribute lists.

Common situations: Copy-pasting reflect attributes and merging the lists by hand; upgrading old code that had `#[reflect(Hash)]` plus a newer custom-function registration added next to it instead of replacing it.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/0d5e4dc144d10f3a. Report an issue: GitHub.