neon-bindings/neon · error

Context is not available in async functions. Try a…

Error message

Context is not available in async functions. Try a `Channel` instead.

What it means

This is a compile-time error from Neon's `#[neon]` class macro emitted by `check_channel`. An `async fn` method declared an owned context type (`Cx` or `FunctionContext`, without a reference) as its context argument. Contexts are only available synchronously; async methods must take an owned `Channel` so work can be scheduled back onto the JavaScript thread. The macro detects any `is_context_type` match on the argument type and redirects the user to `Channel`.

Solutions

  1. Replace the context parameter with an owned `Channel`: `async fn foo(&self, mut cx: Channel)`.
  2. If the body needs `FunctionContext`, make the method synchronous instead of async.
  3. Perform context-dependent work before entering the async section, e.g. extract needed data in a sync method and send results via `Channel`.

Example fix

// before
async fn load(&self, cx: FunctionContext) -> JsResult<JsNumber> {
    ...
}

// after
async fn load(&self, mut cx: Channel) -> JsResult<JsNumber> {
    ...
}
Defensive patterns

Strategy: validation

Validate before calling

// Async methods may never declare a context type (owned or referenced).
fn validate_no_context_in_async(is_async: bool, second_arg_ty: &str) -> Result<(), String> {
    if is_async && (second_arg_ty == "FunctionContext" || second_arg_ty == "Cx"
        || second_arg_ty.starts_with("&mut FunctionContext") || second_arg_ty.starts_with("&mut Cx")) {
        return Err("async methods must take `Channel`, not a context".into());
    }
    Ok(())
}

Type guard

fn is_context_type_name(ty: &str) -> bool {
    let ident = ty.trim_start_matches("&mut ").trim_start_matches("&");
    ident == "FunctionContext" || ident == "Cx" || ident.ends_with("::FunctionContext") || ident.ends_with("::Cx")
}

Prevention

When it happens

Trigger: Writing an exported class method as `async fn foo(&self, cx: FunctionContext)` or `async fn foo(&self, cx: Cx)` — an owned (non-reference) context type in an async method.

Common situations: Converting a sync method to async by just adding `async` and leaving `cx: FunctionContext`; assuming contexts work in async code because they do in sync Neon methods; following outdated examples from older Neon versions.

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/6d5d237704a40e35. Report an issue: GitHub.

Appendix: source

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

        // 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 signature
fn first_arg<'a>(
    opts: &meta::Meta,
    sig: &'a syn::Signature,
) -> syn::Result<Option<&'a syn::PatType>> {
    // Extract the second argument (skip &self)
    let arg = match sig.inputs.iter().nth(1) {
        Some(arg) => arg,

        // If context was forced, error to let the user know the mistake

View on GitHub (pinned to 38960e4381)