neon-bindings/neon · error

in classes must take `self` by value, not `&self` or `&mut…

Error message

{} in classes must take `self` by value, not `&self` or `&mut self`. {}

What it means

Async functions and `#[neon(task)]` methods in neon classes must take `self` by value (`self`), not `&self` or `&mut self`. The instance is cloned and moved to the worker thread, so a reference would point at a temporary clone — misleading at best and, for async futures (which must be `'static` for spawning), simply not allowed.

Solutions

  1. Change the receiver from `&self`/`&mut self` to `self` (take ownership).
  2. If you need mutation, perform the mutation before cloning, or restructure so the task owns its own data.
  3. Clone any needed state inside the method body instead of borrowing it.
  4. Convert back to a regular (non-async, non-task) method if borrowing is essential.

Example fix

// before
#[neon]
impl Counter {
    async fn increment(&self, n: f64) -> JsResult<JsNumber> { ... }
}

// after
#[neon]
impl Counter {
    async fn increment(self, n: f64) -> JsResult<JsNumber> { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check method shape before annotating
fn assert_by_value_self(is_async_or_task: bool, receiver: Option<&str>) -> Result<(), String> {
    if is_async_or_task && receiver.map(|r| r != "self").unwrap_or(false) {
        return Err("async/task methods must take `self` by value".into());
    }
    Ok(())
}

Prevention

When it happens

Trigger: Declaring a method as `async fn work(&self)` or `async fn work(&mut self)` inside a `#[neon] impl` block; declaring `#[neon(task)] fn run(&self)`; either receiver form trips this validation.

Common situations: Porting regular class methods (which freely use `&self`) to `async` or task form; writing idiomatic Rust receiver style out of habit; copying a sync method and only adding `async` without changing the receiver.

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 neon-bindings/neon@38960e4381 (2026-09-13). Data as JSON: /api/errors/5372db80f390deac. Report an issue: GitHub.

Appendix: source

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

        ));
    }

    // 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 {
                    "Since the instance is cloned before moving to the worker thread, taking `&self` would operate on a temporary reference to the clone, which is misleading."
                };
                return Err(syn::Error::new(
                    receiver.span(),
                    format!(
                        "{} in classes must take `self` by value, not `&self` or `&mut self`. {}",
                        method_type, reason
                    ),
                ));
            }
        } else {
            let method_type = if matches!(meta.kind, meta::Kind::AsyncFn) {
                "Async functions"
            } else {
                "Task methods"
            };
            return Err(syn::Error::new(
                sig.span(),
                format!(
                    "{} in classes must take `self` as their first parameter.",
                    method_type

View on GitHub (pinned to 38960e4381)