neon-bindings/neon · error
Expected `&mut Cx` instead of a `Channel` reference.
Error message
Expected `&mut Cx` instead of a `Channel` reference.
What it means
In a `#[neon]`-exported sync class method, the first argument after `&self` was declared as a reference to a `Channel` (e.g. `&Channel` or `&mut Channel`). Sync methods can only take a mutable execution-context reference like `&mut Cx` or `&mut FunctionContext`; `Channel` is only accepted as an owned value in async/task methods. The macro rejects the borrowed `Channel` at compile time with this message.
Solutions
- Replace the `Channel` reference with a mutable context reference: use `&mut Cx` (or `&mut FunctionContext`).
- If you actually need a `Channel`, make the method an async or task method (return a future / use `#[neon]` task support) and take an owned `Channel` (no `&`).
- If the context is inferred (method works without explicit annotation), remove the `Channel` parameter entirely.
- Read the full compile error span: it points at the `Channel` type in the method signature that must change.
Example fix
// before
fn send(cx: &Channel, msg: String) -> JsResult<JsUndefined> { ... }
// after
fn send(cx: &mut Cx, msg: String) -> JsResult<JsUndefined> { ... } Defensive patterns
Strategy: type-guard
Validate before calling
// Before writing the signature, decide sync vs async:
// Sync method -> first param after &self must be &mut Cx / &mut FunctionContext
// Async/Task method -> first param may be owned Channel
fn check_method_sig(is_async: bool, ty: &str) -> Result<(), String> {
if ty.contains("Channel") && ty.starts_with('&') && !is_async {
return Err("borrowed Channel not allowed in sync method; use &mut Cx".into());
}
Ok(())
} Type guard
fn is_channel_reference(sig: &syn::Signature) -> bool {
sig.inputs.iter().nth(1)
.and_then(|a| match a {
syn::FnArg::Typed(p) => Some(matches!(&*p.ty, syn::Type::Reference(r) if type_name(r.elem).ends_with("Channel"))),
_ => None,
})
.unwrap_or(false)
} Prevention
- Use `&mut Cx` for every sync class method context parameter
- Only take `Channel` as an owned parameter in async/task methods
- Copy signatures from neon's documented examples rather than writing from memory
- Let the compiler catch it early: run `cargo check` after signature changes
When it happens
Trigger: Declaring a neon class method signature whose second parameter (after `&self`) is `&Channel`, `&mut Channel`, or `&'a Channel` while the method is compiled as a Normal/Async (sync-context) method and no explicit context option forces the parameter to be a context.
Common situations: Copy-pasting a signature from an async/task method into a sync method; migrating old neon code where `Channel` handling differed; confusing `Channel` with `Cx`/`FunctionContext` when adding a method parameter.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Context parameters must be a `&mut` reference. Try `&mut…
- Unexpected `Channel` in sync method. Use `&mut…
- Must be a `&mut` reference.
- Expected an owned `Channel` instead of a reference.
- Must be a `&mut` reference.
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/d0e87b104ae87d6a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon-macros/src/class/mod.rs:392
_ => Ok((None, None)),
}
}
// Check if a sync method has a context argument (adapted from export function)
// Key difference from #[export]: methods have &self as first param, so context is second param
fn check_context(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {
// Extract the first argument (after &self)
let ty = match first_arg(opts, sig)? {
Some(arg) => arg,
None => return Ok(false),
};
// Extract the reference type
let ty = match &*ty.ty {
// Tried to use a borrowed Channel
syn::Type::Reference(ty) if !opts.context && is_channel_type(&ty.elem) => {
return Err(syn::Error::new(
ty.elem.span(),
"Expected `&mut Cx` instead of a `Channel` reference.",
))
}
syn::Type::Reference(ty) => ty,
// Context needs to be a reference
_ if opts.context || is_context_type(&ty.ty) => {
return Err(syn::Error::new(
ty.ty.span(),
"Context parameters must be a `&mut` reference. Try `&mut FunctionContext` or `&mut Cx`.",
))
}
// Hint that `Channel` should be swapped for `&mut Cx`
_ if is_channel_type(&ty.ty) => {
return Err(syn::Error::new(View on GitHub (pinned to 38960e4381)