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

  1. Replace the `Channel` reference with a mutable context reference: use `&mut Cx` (or `&mut FunctionContext`).
  2. 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 `&`).
  3. If the context is inferred (method works without explicit annotation), remove the `Channel` parameter entirely.
  4. 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

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


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)