neon-bindings/neon · error

Must be a `&mut` reference.

Error message

Must be a `&mut` reference.

What it means

A neon class method or exported function declared its context parameter as an immutable reference (e.g. `&Cx`, `&FunctionContext`). Contexts provide interior-mutable access to the JS runtime, so neon requires them to be `&mut` references. The macro checks mutability of the reference and fails compilation if it is missing.

Solutions

  1. Add `mut` to the reference: `&Cx` -> `&mut Cx`, `&FunctionContext` -> `&mut FunctionContext`.
  2. If the surrounding code requires an immutable borrow, restructure so the context is only held where a `&mut` borrow is allowed (e.g. drop other borrows of the same data).
  3. If the parameter is not actually a context, rename the type or remove the `context` attribute forcing it to be one.
  4. Re-run the build after the fix — this error is purely about the missing `mut` token in the signature.

Example fix

// before
fn get_count(cx: &Cx) -> JsResult<JsNumber> { ... }

// after
fn get_count(cx: &mut Cx) -> JsResult<JsNumber> { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

// Guard: context reference must be mutable
fn validate_mut_context(ty: &str) -> Result<(), String> {
    if (ty.contains("Cx") || ty.contains("FunctionContext")) && ty.starts_with("&") && !ty.starts_with("&mut ") {
        return Err(format!("`{}` must be `&mut` reference", ty));
    }
    Ok(())
}

Type guard

fn is_mut_context_ref(ty: &syn::Type) -> bool {
    matches!(ty, syn::Type::Reference(r) if r.mutability.is_some()
        && ["Cx", "FunctionContext"].iter().any(|c| type_name(&r.elem).contains(c)))
}

Prevention

When it happens

Trigger: Declaring the context parameter as `&Cx`, `&FunctionContext`, `&'a mut`-less borrowed context, or any reference form where `mut` is absent, while the type is recognized as a context (or `#[neon(context)]` forces it).

Common situations: Writing idiomatic shared-reference style `&Cx` parameters; linters or manual refactors removing `mut`; porting code from libraries where contexts are borrowed immutably.

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

Appendix: source

Thrown at crates/neon-macros/src/class/mod.rs:426

        // Hint that `Channel` should be swapped for `&mut Cx`
        _ if is_channel_type(&ty.ty) => {
            return Err(syn::Error::new(
                ty.ty.span(),
                "Unexpected `Channel` in sync method. Use `&mut FunctionContext` for sync methods, or `Channel` in async/task methods.",
            ))
        }

        _ => 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)
}

// Check if an async method has a Channel argument (adapted from export function)
fn check_channel(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),
    };

    // Check the type
    match &*ty.ty {
        // Provided `&mut Channel` instead of `Channel`
        syn::Type::Reference(ty) if opts.context || is_channel_type(&ty.elem) => {

View on GitHub (pinned to 38960e4381)