neon-bindings/neon · error
Expected an owned `Channel` instead of a context reference.
Error message
Expected an owned `Channel` instead of a context reference.
What it means
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.
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.
Example fix
// before
#[neon]
impl Greeter {
async fn greet(&self, cx: &mut FunctionContext) -> JsResult<JsString> {
...
}
}
// after
#[neon]
impl Greeter {
async fn greet(&self, mut cx: Channel) -> JsResult<JsString> {
...
}
} Defensive patterns
Strategy: validation
Validate before calling
// Before compiling, audit exported Neon class methods:
// every `async fn` method must take an owned `Channel`, not `&mut FunctionContext`/`&mut Cx`.
fn validate_async_method_sig(is_async: bool, second_arg_ty: &str) -> Result<(), String> {
if is_async && (second_arg_ty.starts_with("&mut FunctionContext")
|| second_arg_ty.starts_with("&mut Cx")) {
return Err(format!(
"async method must take an owned `Channel`, found `{}`", second_arg_ty
));
}
Ok(())
} Type guard
fn is_owned_channel(ty: &str) -> bool {
ty == "Channel" || ty.ends_with("::Channel")
} Prevention
- 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.
When it happens
Trigger: 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)`.
Common situations: 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`.
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
- Context is not available in async functions. Try a…
- Cannot combine `async fn` with `#[neon(async)]` attribute
- Expected an owned `Channel` instead of a context reference.
- Context is not available in async functions. Try a…
- Unexpected `Channel` in sync method. Use `&mut…
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/78e440d99083b9f9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon-macros/src/class/mod.rs:452
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) => {
Err(syn::Error::new(
ty.span(),
"Expected an owned `Channel` instead of a reference.",
))
}
// Provided a `&mut Cx` instead of a `Channel`
syn::Type::Reference(ty) if is_context_type(&ty.elem) => Err(syn::Error::new(
ty.elem.span(),
"Expected an owned `Channel` instead of a context reference.",
)),
// Found a `Channel`
_ if opts.context || is_channel_type(&ty.ty) => Ok(true),
// Tried to use an owned `Cx`
_ if is_context_type(&ty.ty) => Err(syn::Error::new(
ty.ty.span(),
"Context is not available in async functions. Try a `Channel` instead.",
)),
_ => Ok(false),
}
}
// Extract the first argument (after &self) from a method signatureView on GitHub (pinned to 38960e4381)