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
- Replace the context parameter with an owned `Channel`: `async fn foo(&self, mut cx: Channel)`.
- If the body needs `FunctionContext`, make the method synchronous instead of async.
- 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
- Remember the rule: `Cx`/`FunctionContext` = sync only; `Channel` = async.
- When adding `async` to a method, always swap the context parameter for `Channel` in the same commit.
- Search the codebase for `async fn` in `#[neon]` impl blocks and verify their second parameters.
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
- Expected an owned `Channel` instead of a context reference.
- Cannot combine `async fn` with `#[neon(async)]` attribute
- Expected an owned `Channel` instead of a context reference.
- Context is not available in async functions. Try a…
- Unexpected `Channel` in sync method. Use `&mut…
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 mistakeView on GitHub (pinned to 38960e4381)