{"record":{"id":"78e440d99083b9f9","repo":"neon-bindings/neon","slug":"expected-an-owned-channel-instead-of-a-context-reference","errorCode":null,"errorMessage":"Expected an owned `Channel` instead of a context reference.","messagePattern":"Expected an owned `Channel` instead of a context reference\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/neon-macros/src/class/mod.rs","lineNumber":452,"sourceCode":"fn check_channel(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    // Check the type\n    match &*ty.ty {\n        // Provided `&mut Channel` instead of `Channel`\n        syn::Type::Reference(ty) if opts.context || is_channel_type(&ty.elem) => {\n            Err(syn::Error::new(\n                ty.span(),\n                \"Expected an owned `Channel` instead of a reference.\",\n            ))\n        }\n\n        // Provided a `&mut Cx` instead of a `Channel`\n        syn::Type::Reference(ty) if is_context_type(&ty.elem) => Err(syn::Error::new(\n            ty.elem.span(),\n            \"Expected an owned `Channel` instead of a context reference.\",\n        )),\n\n        // Found a `Channel`\n        _ if opts.context || is_channel_type(&ty.ty) => Ok(true),\n\n        // Tried to use an owned `Cx`\n        _ if is_context_type(&ty.ty) => Err(syn::Error::new(\n            ty.ty.span(),\n            \"Context is not available in async functions. Try a `Channel` instead.\",\n        )),\n\n        _ => Ok(false),\n    }\n}\n\n// Extract the first argument (after &self) from a method signature","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/neon-bindings/neon/blob/38960e4381d9ad13b551cdf2d261f609167c9bc2/crates/neon-macros/src/class/mod.rs#L434-L470","documentation":"This is a compile-time error from Neon's `#[neon]` class macro. When an `async fn` method (or one forced into channel mode by `#[neon(context)]`) declares its context-like first argument as a reference to a context type (e.g. `&mut Cx` or `&mut FunctionContext`), the macro rejects it: async methods must take an owned `Channel`, not a borrowed context, because the context cannot live across an await point. The macro checks the second argument (after `&self`) in `check_channel` and produces this error when that argument is a `Type::Reference` whose element is a context type.","triggerScenarios":"Declaring an exported Neon class method as `async fn` (or under `#[neon(context)]`) whose first non-self parameter is a context reference such as `&mut FunctionContext` or `&mut Cx`, e.g. `async fn foo(&self, cx: &mut FunctionContext)`.","commonSituations":"Porting a synchronous Neon method to `async fn` and leaving the existing `cx: &mut FunctionContext` parameter in place; copy-pasting a sync method signature into an async one; migrating code after upgrading Neon where async methods require `Channel`.","solutions":["Change the parameter to an owned `Channel`: `async fn foo(&self, mut cx: Channel)`.","If you only need synchronous access to the context, make the method non-async and keep `&mut FunctionContext`.","Use `cx.channel()` from a synchronous method to schedule async work instead of holding a context in an async fn."],"exampleFix":"// before\n#[neon]\nimpl Greeter {\n    async fn greet(&self, cx: &mut FunctionContext) -> JsResult<JsString> {\n        ...\n    }\n}\n\n// after\n#[neon]\nimpl Greeter {\n    async fn greet(&self, mut cx: Channel) -> JsResult<JsString> {\n        ...\n    }\n}","handlingStrategy":"validation","validationCode":"// Before compiling, audit exported Neon class methods:\n// every `async fn` method must take an owned `Channel`, not `&mut FunctionContext`/`&mut Cx`.\nfn validate_async_method_sig(is_async: bool, second_arg_ty: &str) -> Result<(), String> {\n    if is_async && (second_arg_ty.starts_with(\"&mut FunctionContext\")\n        || second_arg_ty.starts_with(\"&mut Cx\")) {\n        return Err(format!(\n            \"async method must take an owned `Channel`, found `{}`\", second_arg_ty\n        ));\n    }\n    Ok(())\n}","typeGuard":"fn is_owned_channel(ty: &str) -> bool {\n    ty == \"Channel\" || ty.ends_with(\"::Channel\")\n}","tryCatchPattern":null,"preventionTips":["Adopt the convention: async Neon methods always take `Channel`; sync methods take `&mut FunctionContext`.","When converting a sync method to async, replace the context parameter in the same change.","Run `cargo check` immediately after signature conversions to catch macro errors early."],"tags":["rust","proc-macro","neon","async","channel","compile-time"],"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"}