{"record":{"id":"d0e87b104ae87d6a","repo":"neon-bindings/neon","slug":"expected-mut-cx-instead-of-a-channel-reference","errorCode":null,"errorMessage":"Expected `&mut Cx` instead of a `Channel` reference.","messagePattern":"Expected `&mut Cx` instead of a `Channel` reference\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/neon-macros/src/class/mod.rs","lineNumber":392,"sourceCode":"\n        _ => Ok((None, None)),\n    }\n}\n\n// Check if a sync method has a context argument (adapted from export function)\n// Key difference from #[export]: methods have &self as first param, so context is second param\nfn check_context(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {\n    // Extract the first argument (after &self)\n    let ty = match first_arg(opts, sig)? {\n        Some(arg) => arg,\n        None => return Ok(false),\n    };\n\n    // Extract the reference type\n    let ty = match &*ty.ty {\n        // Tried to use a borrowed Channel\n        syn::Type::Reference(ty) if !opts.context && is_channel_type(&ty.elem) => {\n            return Err(syn::Error::new(\n                ty.elem.span(),\n                \"Expected `&mut Cx` instead of a `Channel` reference.\",\n            ))\n        }\n\n        syn::Type::Reference(ty) => ty,\n\n        // Context needs to be a reference\n        _ if opts.context || is_context_type(&ty.ty) => {\n            return Err(syn::Error::new(\n                ty.ty.span(),\n                \"Context parameters must be a `&mut` reference. Try `&mut FunctionContext` or `&mut Cx`.\",\n            ))\n        }\n\n        // Hint that `Channel` should be swapped for `&mut Cx`\n        _ if is_channel_type(&ty.ty) => {\n            return Err(syn::Error::new(","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/neon-bindings/neon/blob/38960e4381d9ad13b551cdf2d261f609167c9bc2/crates/neon-macros/src/class/mod.rs#L374-L410","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Replace the `Channel` reference with a mutable context reference: use `&mut Cx` (or `&mut FunctionContext`).","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 `&`).","If the context is inferred (method works without explicit annotation), remove the `Channel` parameter entirely.","Read the full compile error span: it points at the `Channel` type in the method signature that must change."],"exampleFix":"// before\nfn send(cx: &Channel, msg: String) -> JsResult<JsUndefined> { ... }\n\n// after\nfn send(cx: &mut Cx, msg: String) -> JsResult<JsUndefined> { ... }","handlingStrategy":"type-guard","validationCode":"// Before writing the signature, decide sync vs async:\n// Sync method -> first param after &self must be &mut Cx / &mut FunctionContext\n// Async/Task method -> first param may be owned Channel\nfn check_method_sig(is_async: bool, ty: &str) -> Result<(), String> {\n    if ty.contains(\"Channel\") && ty.starts_with('&') && !is_async {\n        return Err(\"borrowed Channel not allowed in sync method; use &mut Cx\".into());\n    }\n    Ok(())\n}","typeGuard":"fn is_channel_reference(sig: &syn::Signature) -> bool {\n    sig.inputs.iter().nth(1)\n        .and_then(|a| match a {\n            syn::FnArg::Typed(p) => Some(matches!(&*p.ty, syn::Type::Reference(r) if type_name(r.elem).ends_with(\"Channel\"))),\n            _ => None,\n        })\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Use `&mut Cx` for every sync class method context parameter","Only take `Channel` as an owned parameter in async/task methods","Copy signatures from neon's documented examples rather than writing from memory","Let the compiler catch it early: run `cargo check` after signature changes"],"tags":["rust","neon","compile-time","macro","napi"],"backgroundTag":"type-mismatch","analyzedSha":"38960e4381d9ad13b551cdf2d261f609167c9bc2","analyzedAt":"2026-09-13T09:05:33.640Z","contentChangedAt":"2026-09-13T09:05:33.640Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}