neon-bindings/neon · error

Cannot combine async method with `#[neon(task)]` attribute

Error message

Cannot combine async method with `#[neon(task)]` attribute

What it means

The `#[neon]` macro rejects class methods declared `async` that also carry the `#[neon(task)]` attribute. An async fn is already executed on a separate task internally, so wrapping it again as an explicit task is contradictory and unsupported by the macro's code generation.

Solutions

  1. Remove the `#[neon(task)]` attribute and keep the method `async fn` (async methods are already spawned as tasks).
  2. Or keep `#[neon(task)]` and remove `async`, implementing the work synchronously in the task's `run` method.
  3. Ensure the method takes `self` by value, since both async and task methods require it.

Example fix

// before
#[neon]
impl Foo {
    #[neon(task)]
    async fn compute(&self) -> JsResult<JsNumber> { ... }
}

// after
#[neon]
impl Foo {
    async fn compute(&self) -> JsResult<JsNumber> { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

// Compile-time macro check; guard at code review / CI
fn assert_no_task_on_async(attrs: &[&str], is_async: bool) -> Result<(), String> {
    if is_async && attrs.contains(&"task") {
        return Err("remove #[neon(task)] from async methods".into());
    }
    Ok(())
}

Prevention

When it happens

Trigger: Annotating a single class method with both forms: `#[neon(task)]` (or the combined `#[neon(task = ...)]`) on a method whose signature is `async fn`, e.g. `#[neon] impl Foo { #[neon(task)] async fn work(&self) -> ... }`.

Common situations: Developers migrate a sync task method to async and forget to remove `#[neon(task)]`; copy-pasting a task-based example and converting it to `async fn`; refactorings that add `async` for await-based I/O without revisiting the attribute list.

Understand the failure class

Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.

Related errors


AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13). Data as JSON: /api/errors/a1a6f5d31bc0d1b6. Report an issue: GitHub.

Appendix: source

Thrown at crates/neon-macros/src/class/mod.rs:542

    Some(&segment.ident)
}

// Validate method attributes for common errors and conflicts
fn validate_method_attributes(meta: &meta::Meta, sig: &syn::Signature) -> syn::Result<()> {
    // Check for conflicting async attributes
    if matches!(meta.kind, meta::Kind::AsyncFn) && matches!(meta.kind, meta::Kind::Async) {
        return Err(syn::Error::new(
            sig.span(),
            "Cannot combine `async fn` with `#[neon(async)]` attribute",
        ));
    }

    // Check for async + task conflict
    if matches!(meta.kind, meta::Kind::AsyncFn | meta::Kind::Async)
        && matches!(meta.kind, meta::Kind::Task)
    {
        return Err(syn::Error::new(
            sig.span(),
            "Cannot combine async method with `#[neon(task)]` attribute",
        ));
    }

    // Validate that async fn and task methods take self by value
    if matches!(meta.kind, meta::Kind::AsyncFn | meta::Kind::Task) {
        if let Some(syn::FnArg::Receiver(receiver)) = sig.inputs.first() {
            if receiver.reference.is_some() {
                // This is &self or &mut self, but we need self by value
                let method_type = if matches!(meta.kind, meta::Kind::AsyncFn) {
                    "Async functions"
                } else {
                    "Task methods"
                };
                let reason = if matches!(meta.kind, meta::Kind::AsyncFn) {
                    "This is required because async functions capture `self` in the Future, which must be `'static` for spawning."
                } else {

View on GitHub (pinned to 38960e4381)