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
- Change the receiver from `&self`/`&mut self` to `self` (take ownership).
- If you need mutation, perform the mutation before cloning, or restructure so the task owns its own data.
- Clone any needed state inside the method body instead of borrowing it.
- 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
- Default to `self` (not `&self`) for any async or #[neon(task)] class method.
- Clone needed fields explicitly inside the method body instead of borrowing.
- Read the macro's error reason: it explains the clone-before-move semantics.
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
- Cannot combine async method with `#[neon(task)]` attribute
- in classes must take `self` as their first parameter.
- Constructor methods cannot have a `self` receiver
- Class methods must have a `self` receiver (`&self` or `&mut…
- Exported functions cannot receive `self`.
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_typeView on GitHub (pinned to 38960e4381)