neon-bindings/neon · error

Expected an owned `Channel` instead of a reference.

Error message

Expected an owned `Channel` instead of a reference.

What it means

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.

Solutions

  1. Remove the reference and take the `Channel` by value: `Channel` instead of `&mut Channel` or `&Channel`.
  2. 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.
  3. If a reference was needed to avoid a move, clone the `Channel` (`Channel` is cheaply clonable) and pass the clone by value.
  4. Check the `#[neon(context)]` attribute: remove it if the method should not require a context parameter.

Example fix

// before
fn spawn_work(&self, ch: &mut Channel, data: Vec<u8>) { ... }

// after
fn spawn_work(&self, ch: Channel, data: Vec<u8>) { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

// Guard: async/task methods take owned Channel, never a reference
fn validate_channel_ownership(is_async: bool, first_param_ty: &str) -> Result<(), String> {
    if is_async && first_param_ty.contains("Channel") && first_param_ty.starts_with('&') {
        return Err("async/task methods must take owned Channel (no `&`)".into());
    }
    Ok(())
}

Type guard

fn is_channel_ref(sig: &syn::Signature) -> bool {
    sig.inputs.iter().nth(1)
        .and_then(|a| match a {
            syn::FnArg::Typed(p) => Some(matches!(&*p.ty, syn::Type::Reference(r) if type_name(&r.elem).ends_with("Channel"))),
            _ => None,
        })
        .unwrap_or(false)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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`.

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/15317a018b040862. Report an issue: GitHub.

Appendix: source

Thrown at crates/neon-macros/src/class/mod.rs:445

    }

    // All tests passed!
    Ok(true)
}

// Check if an async method has a Channel argument (adapted from export function)
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.",

View on GitHub (pinned to 38960e4381)