neon-bindings/neon · error

Must be a `&mut` reference.

Error message

Must be a `&mut` reference.

What it means

This compile-time error comes from Neon's `#[neon::export]` macro when a function parameter accepts a context type (`&Cx`/`&mut Cx`-like). The macro's `check_context` validation requires that a context argument be taken by mutable reference (`&mut Cx`). An immutable reference cannot support the interior mutability that Neon's context machinery relies on, so the macro rejects the signature.

Solutions

  1. Change the context parameter to a mutable reference: `fn f(cx: &mut Cx)` instead of `cx: &Cx`.
  2. If the parameter is not actually a context type, rename/retype it so the macro does not interpret it as one.
  3. If the function does not need context, remove the context parameter (and any `context` attribute) entirely.

Example fix

// before
#[neon::export]
fn add1(cx: &Cx, n: f64) -> f64 { n + 1.0 }

// after
#[neon::export]
fn add1(cx: &mut Cx, n: f64) -> f64 { n + 1.0 }
Defensive patterns

Strategy: validation

Validate before calling

// Compile-time guard: assert the export signature takes the context by &mut
fn assert_mut_context(cx: &mut neon::context::CxContext) {}
// use `assert_mut_context(cx);` inside the export fn; the signature itself
// (`&mut CxContext`) is validated by the macro at compile time.

Type guard

fn is_mut_ref<T>(_: &mut T) -> bool { true } // prefer `&mut Cx` in every #[neon::export] signature

Prevention

When it happens

Trigger: Declaring an exported fn (or a context-marked parameter, e.g. `#[context]` or forced `context` option) whose context parameter is spelled `&Cx` or `&CxContext` instead of `&mut Cx`. Detected in `check_context` when `ty.mutability.is_none()`.

Common situations: Copy-pasting a non-export helper signature into an `#[neon::export]` fn; habitually writing `&Cx` because Rust often prefers shared references; upgrading to Neon's new export API after previously using `ModuleContext` arguments.

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

Appendix: source

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

        // Hint that `Channel` should be swapped for `&mut Cx`
        _ if is_channel_type(&ty.ty) => {
            return Err(syn::Error::new(
                ty.ty.span(),
                "Expected `&mut Cx` instead of `Channel`.",
            ))
        }

        _ => return Ok(false),
    };

    // Not a forced or inferred context
    if !opts.context && !is_context_type(&ty.elem) {
        return Ok(false);
    }

    // Context argument must be mutable
    if ty.mutability.is_none() {
        return Err(syn::Error::new(ty.span(), "Must be a `&mut` reference."));
    }

    // All tests passed!
    Ok(true)
}

// Checks if a _async_ function has a Channel argument and if it is valid
// * If the `context` attribute is included, must be at least one argument
// * Inferred to be channel if named `Channel`
// * Channel argument must not be a reference
// * First argument must not be `FunctionContext` or `Cx`
// * Must not be a `self` receiver
fn check_channel(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),
    };

View on GitHub (pinned to 38960e4381)