{"record":{"id":"6d5d237704a40e35","repo":"neon-bindings/neon","slug":"context-is-not-available-in-async-functions-try-a-channel","errorCode":null,"errorMessage":"Context is not available in async functions. Try a `Channel` instead.","messagePattern":"Context is not available in async functions\\. Try a `Channel` instead\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/neon-macros/src/class/mod.rs","lineNumber":461,"sourceCode":"        // Provided `&mut Channel` instead of `Channel`\n        syn::Type::Reference(ty) if opts.context || is_channel_type(&ty.elem) => {\n            Err(syn::Error::new(\n                ty.span(),\n                \"Expected an owned `Channel` instead of a reference.\",\n            ))\n        }\n\n        // Provided a `&mut Cx` instead of a `Channel`\n        syn::Type::Reference(ty) if is_context_type(&ty.elem) => Err(syn::Error::new(\n            ty.elem.span(),\n            \"Expected an owned `Channel` instead of a context reference.\",\n        )),\n\n        // Found a `Channel`\n        _ if opts.context || is_channel_type(&ty.ty) => Ok(true),\n\n        // Tried to use an owned `Cx`\n        _ if is_context_type(&ty.ty) => Err(syn::Error::new(\n            ty.ty.span(),\n            \"Context is not available in async functions. Try a `Channel` instead.\",\n        )),\n\n        _ => Ok(false),\n    }\n}\n\n// Extract the first argument (after &self) from a method signature\nfn first_arg<'a>(\n    opts: &meta::Meta,\n    sig: &'a syn::Signature,\n) -> syn::Result<Option<&'a syn::PatType>> {\n    // Extract the second argument (skip &self)\n    let arg = match sig.inputs.iter().nth(1) {\n        Some(arg) => arg,\n\n        // If context was forced, error to let the user know the mistake","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/neon-bindings/neon/blob/38960e4381d9ad13b551cdf2d261f609167c9bc2/crates/neon-macros/src/class/mod.rs#L443-L479","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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.","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`."],"exampleFix":"// before\nasync fn load(&self, cx: FunctionContext) -> JsResult<JsNumber> {\n    ...\n}\n\n// after\nasync fn load(&self, mut cx: Channel) -> JsResult<JsNumber> {\n    ...\n}","handlingStrategy":"validation","validationCode":"// Async methods may never declare a context type (owned or referenced).\nfn validate_no_context_in_async(is_async: bool, second_arg_ty: &str) -> Result<(), String> {\n    if is_async && (second_arg_ty == \"FunctionContext\" || second_arg_ty == \"Cx\"\n        || second_arg_ty.starts_with(\"&mut FunctionContext\") || second_arg_ty.starts_with(\"&mut Cx\")) {\n        return Err(\"async methods must take `Channel`, not a context\".into());\n    }\n    Ok(())\n}","typeGuard":"fn is_context_type_name(ty: &str) -> bool {\n    let ident = ty.trim_start_matches(\"&mut \").trim_start_matches(\"&\");\n    ident == \"FunctionContext\" || ident == \"Cx\" || ident.ends_with(\"::FunctionContext\") || ident.ends_with(\"::Cx\")\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["rust","proc-macro","neon","async","channel","compile-time"],"backgroundTag":"type-mismatch","analyzedSha":"38960e4381d9ad13b551cdf2d261f609167c9bc2","analyzedAt":"2026-09-13T09:05:33.640Z","contentChangedAt":"2026-09-13T09:05:33.640Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}