linebender/druid · error

expected str, found... something else

Error message

expected str, found... something else

What it means

parse_lit_into_expr_path converts a literal into a syn::ExprPath and only accepts string literals. When a derive attribute supplies a path value that is not a quoted string (e.g. a bare ident or integer), this compile-time error is raised at the literal's span. The humorous message acknowledges any non-str literal kind.

Solutions

  1. Quote the path value: use name = "MyPath" instead of name = MyPath.
  2. Check which options expect string literals in this derive and wrap their values accordingly.
  3. Inspect the error span — it points at the literal; make that token a string literal.

Example fix

// before
#[lens(name = my_lens)]
count: usize,
// after
#[lens(name = "my_lens")]
count: usize,
Defensive patterns

Strategy: validation

Validate before calling

fn validate_path_value_is_str(opt_value: &str) -> Result<(), String> {
    if opt_value.starts_with('"') && opt_value.ends_with('"') { Ok(()) }
    else { Err(format!("path option must be a quoted string, got: {}", opt_value)) }
}

Prevention

When it happens

Trigger: Passing an unquoted path to an attribute option that expects Lit::Str and is parsed via parse_lit_into_expr_path (called from parse_ast), e.g. a lens/data option given as a bare identifier instead of "identifier".

Common situations: Forgetting quotes around path values in helper attributes, confusing string-valued options with plain path attributes, migrating from crates where paths are written unquoted.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

        match self.ident {
            FieldIdent::Named(ref s) => Ident::new(s, Span::call_site()).into(),
            FieldIdent::Unnamed(num) => Literal::usize_unsuffixed(num).into(),
        }
    }

    pub fn ident_string(&self) -> String {
        match self.ident {
            FieldIdent::Named(ref s) => s.clone(),
            FieldIdent::Unnamed(num) => num.to_string(),
        }
    }
}

fn parse_lit_into_expr_path(lit: &syn::Lit) -> Result<ExprPath, Error> {
    let string = if let syn::Lit::Str(lit) = lit {
        lit
    } else {
        return Err(Error::new(
            lit.span(),
            "expected str, found... something else",
        ));
    };

    let tokens = syn::parse_str(&string.value())?;
    syn::parse2(tokens)
}

fn parse_lit_into_ident(lit: &syn::Lit) -> Result<Ident, Error> {
    let ident = if let syn::Lit::Str(lit) = lit {
        Ident::new(&lit.value(), lit.span())
    } else {
        return Err(Error::new(
            lit.span(),
            "expected str, found... something else",
        ));
    };

View on GitHub (pinned to 0f8b1195e4)