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
The export macro detected that a parameter declared as `&mut Cx` (or another context reference type) was expected to be a `Channel` — e.g. under an async/queued signature where Neon accepts an owned `Channel` instead of the execution context. Unlike error 41 (a reference to a channel type), this variant fires when the referenced type is a *context* type, giving a more specific message pointing the developer at the real type they misused.
Solutions
- Replace `&mut Cx` with an owned `Channel` parameter: `fn f(ch: Channel)`.
- If context access is truly needed, make the function synchronous rather than async/queued.
- If it should be a context param, add the `context` attribute so `check_context` (which allows `&mut` context refs) validates it instead.
Example fix
// before
#[neon::export]
async fn work(cx: &mut Cx) -> JsResult<JsNumber> { /* ... */ }
// after
#[neon::export]
async fn work(ch: Channel) -> JsResult<JsNumber> { /* ... */ } Defensive patterns
Strategy: validation
Validate before calling
// Async/queued exports take Channel, not context refs:
fn valid_async_export(ch: neon::event::Channel) {} // ok
// fn invalid_async_export(cx: &mut Cx) {} // rejected by check_channel Prevention
- For async exports, take `Channel` as the parameter, never `&mut Cx`.
- Check whether you want the `context` attribute (which routes to check_context) before writing a context-typed param.
- Review Neon's async section: the JS context is unavailable; only a Channel can hop back.
When it happens
Trigger: Writing `fn f(cx: &mut Cx)` where the macro requires an owned `Channel` (e.g. an async fn signature matched by `check_channel` when `is_context_type(&ty.elem)`), or `#[context]`-annotated param whose type is a context reference but the caller expected a Channel.
Common situations: Using the familiar `&mut Cx` context parameter in an async fn, where Neon only supports a `Channel`; mixing up context and channel conventions after reading docs for synchronous exports.
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…
- Unexpected `Channel` in sync method. Use `&mut…
- Expected an owned `Channel` instead of a reference.
- Expected an owned `Channel` instead of a context reference.
- Context is not available in async functions. Try a…
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/b94caf119d2c68ed.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon-macros/src/export/function/mod.rs:270
fn check_channel(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {
// Extract the first argument
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, that may be a context, of a functionView on GitHub (pinned to 38960e4381)