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

  1. Replace `&mut Cx` with an owned `Channel` parameter: `fn f(ch: Channel)`.
  2. If context access is truly needed, make the function synchronous rather than async/queued.
  3. 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

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


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 function

View on GitHub (pinned to 38960e4381)