risingwavelabs/risingwave · error
expected literal
Error message
expected literal
What it means
When parsing the attribute list of a `#[function(...)]` declaration, each recognized key (`batch_fn`, `state`, `prebuild`, etc.) must be given a string literal value via `get_value()`. If the value after `=` is an expression but not a literal (e.g. a path, macro call, or concatenation), the parser emits this spanned error.
Source
Thrown at src/expr/macro/src/parse.rs:62
parsed.name = name.trim().to_owned();
parsed.args = if args.is_empty() {
vec![]
} 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()?);View on GitHub (pinned to 6469eb736d)
Solutions
- Replace the expression with a plain string literal: `batch_fn = "..."`.
- Inline whatever the constant or macro produced as a literal string.
- Compute the value at build time with a build script only if truly dynamic — the attribute itself must stay a literal.
Example fix
// before
#[function("my_add(int4, int4)", batch_fn = MY_BATCH_FN)]
// after
#[function("my_add(int4, int4)", batch_fn = "my_add_batch")] Defensive patterns
Strategy: validation
Validate before calling
// Attribute values must be plain string literals
macro_rules! check_attr { ($k:literal = $v:expr) => { const { assert!(matches!($v, _: &'static str)); } } }
// Simplest guard: review that every key=value uses quotes:
// GOOD: batch_fn = "name" BAD: batch_fn = some_const Prevention
- Always write attribute values as double-quoted string literals; no constants, no concat!(), no paths.
- If you need reuse, generate the whole #[function] invocation from a declarative macro instead of parameterizing the value.
- Compile early with cargo check on the expr crate after editing attributes.
When it happens
Trigger: Writing `#[function("sig(int4)", batch_fn = some_const)]`, `prebuild = concat!("a", "b")`, or any non-literal expression after a key that requires a string value.
Common situations: Developers trying to share/configure attribute values through constants or macro-generated strings, which Rust proc-macro attribute parsing here does not support.
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
- expected string literal
- invalid property: {:?}
- You can't use the macro on this type
- Expected #[serde_prefix_all(skip)]
- type inference function cannot be automatically derived. You
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/6c42e9b48660ad1c.
Report an issue: GitHub.