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
- Add `mut` to the reference: `&Cx` -> `&mut Cx`, `&FunctionContext` -> `&mut FunctionContext`.
- 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).
- If the parameter is not actually a context, rename the type or remove the `context` attribute forcing it to be one.
- 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
- Treat every neon context parameter as `&mut` by default
- Do not run lints/fixes that strip `mut` from context references
- Structure code so no conflicting immutable borrows force you to drop `mut`
- Compile early and often — this is a one-token fix caught by the macro
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
- Expected `&mut Cx` instead of a `Channel` reference.
- Context parameters must be a `&mut` reference. Try `&mut…
- Unexpected `Channel` in sync method. Use `&mut…
- 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/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)