{"record":{"id":"15317a018b040862","repo":"neon-bindings/neon","slug":"expected-an-owned-channel-instead-of-a-reference","errorCode":null,"errorMessage":"Expected an owned `Channel` instead of a reference.","messagePattern":"Expected an owned `Channel` instead of a reference\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/neon-macros/src/class/mod.rs","lineNumber":445,"sourceCode":"    }\n\n    // All tests passed!\n    Ok(true)\n}\n\n// Check if an async method has a Channel argument (adapted from export function)\nfn 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.\",","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/neon-bindings/neon/blob/38960e4381d9ad13b551cdf2d261f609167c9bc2/crates/neon-macros/src/class/mod.rs#L427-L463","documentation":"In an async or task neon method (where `check_channel` applies), the first argument after `&self` was declared as a reference to a `Channel` (e.g. `&mut Channel`). Async/task methods must receive an owned `Channel`, because it is created from the context and moved into the spawned future/thread; a borrowed `Channel` cannot outlive the method call. The macro rejects references with this error.","triggerScenarios":"Declaring an `#[neon]` AsyncFn/Task method whose first non-self parameter is `&Channel` or `&mut Channel`, or using `#[neon(context)]` on such a method with any reference-typed first argument.","commonSituations":"Adding `&` by reflex when defining method parameters; converting a sync method that took `&mut Channel` into an async/task method without dropping the reference; misunderstanding that async methods take ownership of the `Channel`.","solutions":["Remove the reference and take the `Channel` by value: `Channel` instead of `&mut Channel` or `&Channel`.","If you actually wanted the execution context, use `&mut Cx` only in sync methods — in async/task methods use an owned `Channel` and obtain context inside the closure/future as needed.","If a reference was needed to avoid a move, clone the `Channel` (`Channel` is cheaply clonable) and pass the clone by value.","Check the `#[neon(context)]` attribute: remove it if the method should not require a context parameter."],"exampleFix":"// before\nfn spawn_work(&self, ch: &mut Channel, data: Vec<u8>) { ... }\n\n// after\nfn spawn_work(&self, ch: Channel, data: Vec<u8>) { ... }","handlingStrategy":"type-guard","validationCode":"// Guard: async/task methods take owned Channel, never a reference\nfn validate_channel_ownership(is_async: bool, first_param_ty: &str) -> Result<(), String> {\n    if is_async && first_param_ty.contains(\"Channel\") && first_param_ty.starts_with('&') {\n        return Err(\"async/task methods must take owned Channel (no `&`)\".into());\n    }\n    Ok(())\n}","typeGuard":"fn is_channel_ref(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":["In async/task methods always declare `ch: Channel` (owned, no `&`)","Clone the Channel instead of borrowing it when ownership is inconvenient","Do not use #[neon(context)] on async/task methods that take a Channel reference","Run `cargo check` after converting methods between sync and async forms"],"tags":["rust","neon","compile-time","macro","async"],"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"}