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

Neon's export macro rejects any attempt to use the execution context (`Cx`/context type) in an `async` exported function, because the JS context is not available on the async executor's thread. Instead, Neon requires a `Channel` to schedule work back on the JavaScript thread. `check_channel` fires this error when an owned (non-reference) parameter is a context type.

Solutions

  1. Replace the `Cx` parameter with an owned `Channel`: `async fn f(ch: Channel)`.
  2. Use `ch.send(...)` or clone the channel into the future to hop back to the JS thread when you need to run JS work.
  3. If you genuinely need the context, convert the export to a synchronous function.

Example fix

// before
#[neon::export]
async fn fetch(cx: Cx, url: String) -> JsResult<JsString> { /* ... */ }

// after
#[neon::export]
async fn fetch(ch: Channel, url: String) -> JsResult<JsString> { /* ... */ }
Defensive patterns

Strategy: fallback

Validate before calling

// Reject Cx params in async exports before compiling:
// prefer: async fn f(ch: Channel)   — schedule JS work via ch.send()
// avoid: async fn f(cx: Cx)

Prevention

When it happens

Trigger: Declaring `#[neon::export] async fn f(cx: Cx)` — an owned context type parameter in an async fn, matched by `_ if is_context_type(&ty.ty)`.

Common situations: Writing async exports for the first time and copying the sync signature (`cx: &mut Cx`) while dropping the reference; trying to execute JS work directly inside a `Future` running off the main thread.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13). Data as JSON: /api/errors/da2f9b942e0e7a30. Report an issue: GitHub.

Appendix: source

Thrown at crates/neon-macros/src/export/function/mod.rs:279

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

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

View on GitHub (pinned to 38960e4381)