neon-bindings/neon · error
Unexpected second receiver argument.
Error message
Unexpected second receiver argument.
What it means
Compile-time error from Neon's `#[neon]` class macro, raised by `first_arg` when the second entry in a method's inputs (after `&self`) is another receiver (`self`-style argument, `syn::FnArg::Receiver`) rather than a normally typed parameter. Rust does not normally allow two receivers, so the macro treats this as an unexpected signature shape and fails with "Unexpected second receiver argument."
Solutions
- Remove the second receiver; a method can only have one `self` receiver.
- Replace the second receiver with a properly typed context parameter, e.g. `cx: &mut FunctionContext`.
- If generated by another macro or tool, fix the generator to emit a typed second argument.
Example fix
// before
fn update(&self, &mut self) -> JsResult<JsUndefined> {
...
}
// after
fn update(&self, cx: &mut FunctionContext) -> JsResult<JsUndefined> {
...
} Defensive patterns
Strategy: validation
Validate before calling
// A method signature may contain at most one receiver and it must be first.
fn validate_single_receiver(inputs: &[String]) -> Result<(), String> {
if inputs.iter().skip(1).any(|t| t.contains("self")) {
return Err("method has a second receiver argument; replace it with a typed parameter".into());
}
Ok(())
} Type guard
fn second_arg_is_typed(inputs: &[String]) -> bool {
inputs.get(1).map(|t| !t.contains("self")).unwrap_or(true)
} Prevention
- Never write `self` twice in a method signature; Rust itself only permits one receiver.
- When generating signatures programmatically, always emit typed parameters after the receiver.
- Treat `&mut self` appearing after the first parameter as a typo for a context argument like `cx: &mut FunctionContext`.
When it happens
Trigger: Declaring an exported class method with two self-like receivers, e.g. `fn foo(&self, &mut self)` or another receiver form appearing as the second input of the method signature processed by the macro.
Common situations: Hand-written or macro-generated signatures that mistakenly include a second `self`/`&mut self` parameter; typos where an intended typed parameter was written as `&mut self`; code generated by refactoring tools that duplicated the receiver.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Expected an owned `Channel` instead of a context reference.
- Context is not available in async functions. Try a…
- Expected a context argument after `&self` when using…
- Cannot combine `async fn` with `#[neon(async)]` attribute
- Constructor cannot have a `self` receiver
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/ff41f06fd6459015.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon-macros/src/class/mod.rs:493
// Extract the second argument (skip &self)
let arg = match sig.inputs.iter().nth(1) {
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 after `&self` when using `#[neon(context)]`. Add a parameter like `cx: &mut FunctionContext` or remove the `context` attribute.",
))
}
None => return Ok(None),
};
// Expect a typed pattern; self receivers are not supported (but shouldn't appear here)
match arg {
syn::FnArg::Typed(ty) => Ok(Some(ty)),
syn::FnArg::Receiver(arg) => Err(syn::Error::new(
arg.span(),
"Unexpected second receiver argument.",
)),
}
}
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)