risingwavelabs/risingwave · error

expected string literal

Error message

expected string literal

What it means

Same `get_value()` closure as the "expected literal" check, but one level deeper: the value must be a literal AND a string literal (`syn::Lit::Str`). Integers, booleans, chars, or byte strings after an attribute key produce this spanned error.

Source

Thrown at src/expr/macro/src/parse.rs:65

        } else {
            args.split(',').map(|s| s.trim().to_owned()).collect()
        };
        parsed.ret = ret.trim().to_owned();
        parsed.is_table_function = is_table_function;

        if input.parse::<Token![,]>().is_err() {
            return Ok(parsed);
        }

        let metas = input.parse_terminated(syn::Meta::parse, Token![,])?;
        for meta in metas {
            let get_value = || {
                let kv = meta.require_name_value()?;
                let syn::Expr::Lit(lit) = &kv.value else {
                    return Err(Error::new(kv.value.span(), "expected literal"));
                };
                let syn::Lit::Str(lit) = &lit.lit else {
                    return Err(Error::new(kv.value.span(), "expected string literal"));
                };
                Ok(lit.value())
            };
            if meta.path().is_ident("batch_fn") {
                parsed.batch_fn = Some(get_value()?);
            } else if meta.path().is_ident("state") {
                parsed.state = Some(get_value()?);
            } else if meta.path().is_ident("init_state") {
                parsed.init_state = Some(get_value()?);
            } else if meta.path().is_ident("prebuild") {
                parsed.prebuild = Some(get_value()?);
            } else if meta.path().is_ident("type_infer") {
                parsed.type_infer = Some(get_value()?);
            } else if meta.path().is_ident("generic") {
                parsed.generic = Some(get_value()?);
            } else if meta.path().is_ident("volatile") {
                parsed.volatile = true;
            } else if meta.path().is_ident("deprecated") || meta.path().is_ident("internal") {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Wrap the value in double quotes so it is a Rust string literal: `type_infer = "infer_type"`.
  2. Check the expected format of each key in existing `#[function]` usages and match it.
  3. If passing a type, express it as a string the macro can parse, e.g. `state = "MyState"`.

Example fix

// before
#[function("avg(int4)", state = MyState)]

// after
#[function("avg(int4)", state = "MyState")]
Defensive patterns

Strategy: validation

Validate before calling

// Every value for batch_fn/state/init_state/prebuild/type_infer/generic must be a quoted string:
fn is_string_literal_attr(v: &str) -> bool { v.starts_with('"') && v.ends_with('"') }
assert!(is_string_literal_attr("\"MyState\""));

Prevention

When it happens

Trigger: Writing `#[function("sig(int4)", type_infer = 42)]`, `state = true`, `prebuild = 'c'`, or any non-string literal for keys `batch_fn`, `state`, `init_state`, `prebuild`, `type_infer`, or `generic`.

Common situations: Developers passing type identifiers or numeric flags instead of quoted type/function-name strings when declaring function attributes.

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 risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/8647e2c04e980719. Report an issue: GitHub.