linebender/druid · error

The 'druid' attribute has been replaced with separate…

Error message

The 'druid' attribute has been replaced with separate 'lens' and 'data' attributes.

What it means

The derive code for the `data` attribute parsing panics when it encounters the deprecated `#[druid(...)]` field attribute. The `druid` attribute namespace was split into separate `lens` and `data` attributes, and old usage is rejected outright rather than silently migrated.

Solutions

  1. Replace `#[druid(data = ...)]` with `#[data(...)]` on the field
  2. Replace any `#[druid(lens = ...)]` usage with `#[lens(...)]`
  3. Consult the druid-derive changelog for the attribute split migration

Example fix

// before
#[derive(Data)]
struct S { #[druid(data = "true")] flag: bool }

// after
#[derive(Data)]
struct S { #[data(flag)] flag: bool }
Defensive patterns

Strategy: validation

Validate before calling

// grep for the deprecated attribute before compiling:
// grep -rn '#\[druid(' src/

Prevention

When it happens

Trigger: A struct field carries `#[druid(...)]` (matched by `BASE_DRUID_DEPRECATED_ATTR_PATH`) while the macro parses attributes to build `DataAttr`, inside `parse_ast`.

Common situations: Upgrading druid from an older version where `#[druid(...)]` was the documented attribute; copied example code from old tutorials or blog posts.

Understand the failure class

Background: "is deprecated and will be removed" — deprecation warnings for old API names, keywords, and options, and how to migrate before the removal release — this error's family across 29 libraries.

Related errors


AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10). Data as JSON: /api/errors/ee97888ed149a72a. Report an issue: GitHub.

Appendix: source

Thrown at druid-derive/src/attr.rs:131

    pub fn iter(&self) -> impl Iterator<Item = &Field<Attrs>> {
        self.fields.iter()
    }
}

impl Field<DataAttr> {
    pub fn parse_ast(field: &syn::Field, index: usize) -> Result<Self, Error> {
        let ident = match field.ident.as_ref() {
            Some(ident) => FieldIdent::Named(ident.to_string().trim_start_matches("r#").to_owned()),
            None => FieldIdent::Unnamed(index),
        };

        let ty = field.ty.clone();

        let mut data_attr = DataAttr::Empty;
        for attr in field.attrs.iter() {
            if attr.path.is_ident(BASE_DRUID_DEPRECATED_ATTR_PATH) {
                panic!(
                    "The 'druid' attribute has been replaced with separate \
                    'lens' and 'data' attributes.",
                );
            } else if attr.path.is_ident(BASE_DATA_ATTR_PATH) {
                match attr.parse_meta()? {
                    Meta::List(meta) => {
                        assert!(
                            meta.nested.len() <= 1,
                            "only single data attribute is allowed"
                        );
                        if let Some(nested) = meta.nested.first() {
                            match nested {
                                NestedMeta::Meta(Meta::Path(path))
                                    if path.is_ident(IGNORE_ATTR_PATH) =>
                                {
                                    data_attr = DataAttr::Ignore;
                                }
                                NestedMeta::Meta(Meta::NameValue(meta))

View on GitHub (pinned to 0f8b1195e4)