neon-bindings/neon · error
Exported functions cannot receive `self`.
Error message
Exported functions cannot receive `self`.
What it means
The `#[neon::export]` macro refuses Rust functions whose first parameter is a `self` receiver (e.g. `fn foo(&self)`). Exported functions become plain JavaScript-callable functions, so they have no object instance to dispatch against; the macro's `first_arg` check inspects the first argument and rejects `FnArg::Receiver`.
Solutions
- Remove the `self` parameter and make the exported item a free function, taking needed values as explicit arguments
- If exporting a method-like API, export a free constructor function and call methods internally, or use a class (impl block) export instead
- Move the attribute off the method and export a wrapper free function that constructs the receiver
- Use plain `#[napi]`-style conventions: export only free functions with JS-visible argument types
Example fix
// before
struct Counter { n: u32 }
impl Counter {
#[neon::export]
fn increment(&self) -> u32 { self.n + 1 }
}
// after
struct Counter { n: u32 }
#[neon::export]
fn increment(n: f64) -> f64 { n + 1.0 } Defensive patterns
Strategy: validation
Validate before calling
function assertExportableFn(fn) { if (typeof fn !== 'function') throw new TypeError('export target must be a free function'); } Prevention
- Only annotate free functions (no self receivers) with #[neon::export]
- Check the compile error location: it points at the receiver argument; remove self or restructure
- Prefer exporting impl blocks (classes) for instance-style APIs rather than methods with #[neon::export]
When it happens
Trigger: Applying `#[neon::export]` (via `check_context`/`check_channel` path) to an inherent or trait method that takes `self`, `&self`, `&mut self`, or `self` as its first parameter.
Common situations: Refactoring an existing Rust struct method to be exported by just adding the attribute; writing `impl MyStruct { #[neon::export] fn create(&self) {...} }`; confusing Rust methods with the macro's supported standalone functions, consts, statics, and impl blocks.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- in classes must take `self` by value, not `&self` or `&mut…
- Constructor methods cannot have a `self` receiver
- Class methods must have a `self` receiver (`&self` or `&mut…
- `neon::export` can only be applied to functions, consts…
- class must be implemented for a type name
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/1f8f5513cba36a53.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon-macros/src/export/function/mod.rs:311
// Extract the first argument
let arg = match sig.inputs.first() {
Some(arg) => arg,
// If context was forced, error to let the user know the mistake
None if opts.context => {
return Err(syn::Error::new(
sig.inputs.span(),
"Expected a context argument. Try removing the `context` attribute.",
))
}
None => return Ok(None),
};
// Expect a typed pattern; self receivers are not supported
match arg {
syn::FnArg::Typed(ty) => Ok(Some(ty)),
syn::FnArg::Receiver(arg) => Err(syn::Error::new(
arg.span(),
"Exported functions cannot receive `self`.",
)),
}
}
fn is_context_type(ty: &syn::Type) -> bool {
let ident = match type_path_ident(ty) {
Some(ident) => ident,
None => return false,
};
ident == "FunctionContext" || ident == "Cx"
}
fn is_channel_type(ty: &syn::Type) -> bool {
let ident = match type_path_ident(ty) {
Some(ident) => ident,View on GitHub (pinned to 38960e4381)