PyO3/pyo3 · error · syn::Error

missing `message` in `warn` attribute

Error message

missing `message` in `warn` attribute

What it means

The #[pyo3(warn(...))] function attribute requires a `message` string. After parsing optional category and message key-value pairs, if no `message = "..."` was provided the parser fails with this syn::Error, since pyo3 always needs a warning message to emit.

Source

Thrown at pyo3-macros-backend/src/pyfunction.rs:215

            if lookahead.peek(attributes::kw::message) {
                message = content
                    .parse::<PyFunctionWarningMessageAttribute>()
                    .map(Some)?;
            } else if lookahead.peek(attributes::kw::category) {
                category = content
                    .parse::<PyFunctionWarningCategoryAttribute>()
                    .map(Some)?;
            } else {
                return Err(lookahead.error());
            }

            if content.peek(Token![,]) {
                content.parse::<Token![,]>()?;
            }
        }

        Ok(PyFunctionWarningAttribute {
            message: message.ok_or(syn::Error::new(
                content.span(),
                "missing `message` in `warn` attribute",
            ))?,
            category,
            span,
        })
    }
}

impl ToTokens for PyFunctionWarningAttribute {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let message_tokens = self.message.to_token_stream();
        let category_tokens = self
            .category
            .as_ref()
            .map_or(quote! {}, |cat| cat.to_token_stream());

        let token_stream = quote! {

View on GitHub (pinned to ac9b6899d3)

Solutions

  1. Add `message = "..."` inside the warn attribute
  2. Keep the category if desired: warn(category, message = "...")
  3. Remove the warn attribute if no warning was intended

Example fix

// before
#[pyo3(warn(PendingDeprecationWarning))]
#[pyfunction]
fn old_api() {}
// after
#[pyo3(warn(PendingDeprecationWarning, message = "old_api is deprecated"))]
#[pyfunction]
fn old_api() {}
Defensive patterns

Strategy: validation

Validate before calling

// before compiling, verify warn attributes carry a message
if let Some(i) = attr.find("warn(") {
    assert!(attr[i..].contains("message"), "warn needs message = \"...\"");
}

Prevention

When it happens

Trigger: Writing `#[pyo3(warn(DeprecationWarning))]` or `#[pyo3(warn)]` without `message = "..."` on a #[pyfunction].

Common situations: Developers copying only the warning category from docs/examples that show a fuller form; assuming the category alone is sufficient as with Python's warnings.warn defaults.

Related errors


AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05). Data as JSON: /api/errors/ef1504b60b44a5fe. Report an issue: GitHub.