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

Compile-time error from Neon's `#[neon] fn` macro's context-type check. A function parameter was declared as a borrowed `&Channel` (or reference to Channel), but Neon requires the execution context parameter to be `&mut Cx`. Channels cannot be used as a borrowed context argument.

Solutions

  1. Change the parameter to a mutable context reference: `fn my_fn(mut cx: &mut Cx)` (or `FunctionContext`).
  2. If you need a Channel for async work, create/obtain it inside the function from the context (`cx.channel()`), not as a parameter type.
  3. Do not wrap `Channel` in a plain borrow; if a stored Channel is needed, take it as an owned/moved argument or via `JsFunction` arguments, not as the context slot.

Example fix

// before
fn my_fn(channel: &Channel) -> JsResult<JsUndefined> { ... }
// after
fn my_fn(mut cx: &mut FunctionContext) -> JsResult<JsUndefined> {
    let channel = cx.channel();
    ...
}
Defensive patterns

Strategy: type-guard

Type guard

// Ensure the context parameter is a mutable reference, never a Channel reference:
fn uses_valid_context(cx: &mut neon::context::FunctionContext) {}
// Prefer `&mut Cx`/`&mut FunctionContext` for the first parameter; obtain channels via cx.channel().

Try / catch

// Compile-time error; pattern for CI gating:
// if `cargo check` output contains "Expected `&mut Cx` instead of a `Channel` reference."
// then fail the build with a pointer to the offending fn signature.

Prevention

When it happens

Trigger: Declaring a Neon exported function like `fn my_fn(mut cx: &Channel)` or taking `&Channel` as a parameter where the macro expects a context type — specifically when the parameter is a `Type::Reference` whose element `is_channel_type()` and no `#[context]`-style opts flag is set.

Common situations: Developers trying to pass a Channel to background tasks directly as an argument type, confusing `Channel` with `Cx`/`FunctionContext`, or copying signatures from async examples that spawn channels elsewhere.

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/d05309dd0294b902. Report an issue: GitHub.

Appendix: source

Thrown at crates/neon-macros/src/export/function/mod.rs:205

// Checks if a _sync_ function has a context argument and if it is valid
// * If the `context` attribute is included, must be at least one argument
// * Inferred to be context if named `FunctionContext` or `Cx`
// * Context argument must be a `&mut` reference
// * First argument must not be `Channel`
// * Must not be a `self` receiver
fn check_context(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {
    // Extract the first argument
    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 must be a `&mut` reference.",
            ))
        }

        // 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)