neon-bindings/neon · error
Only one `new` constructor is allowed in a class.
Error message
Only one `new` constructor is allowed in a class.
What it means
group_class_items in crates/neon-macros/src/class/mod.rs walks the items of a `#[impl]` class block and collects the constructor. Because a JavaScript class can only have one `new`, Neon permits at most one function named `new` per class; encountering a second one while a constructor was already recorded produces this error at the duplicate item's span.
Solutions
- Keep exactly one `new` constructor in the class impl
- Move extra logic into private helper functions with different names and call them from the single constructor
- If you need multiple construction paths, accept different argument shapes in the one constructor (e.g. an options object) or provide named static factory methods marked `#[method]`
- Rename any non-constructor helper that happens to be called `new`
Example fix
// before
impl MyCoord {
#[constructor]
fn new(cx: &mut FunctionContext) -> JsResult<JsMyCoord> { ... }
#[constructor]
fn new_from_xy(cx: &mut FunctionContext) -> JsResult<JsMyCoord> { ... }
}
// after
impl MyCoord {
#[constructor]
fn new(cx: &mut FunctionContext) -> JsResult<JsMyCoord> { ... }
#[method]
fn from_xy(cx: &mut FunctionContext) -> JsResult<JsMyCoord> { ... }
} Defensive patterns
Strategy: validation
Validate before calling
fn ensure_single_constructor(items: &[syn::ImplItem]) -> Result<(), String> {
let count = items.iter().filter(|i| matches!(i, syn::ImplItem::Fn(f) if f.sig.ident == "new")).count();
if count > 1 { Err(format!("{} constructors named `new` found", count)) } else { Ok(()) }
} Prevention
- One `new` per class; use differently named `#[method]` factories for alternate construction paths
- Grep impl blocks for duplicate `fn new` before building
- Avoid naming private helpers `new`; prefer `default_`, `build_` prefixes
When it happens
Trigger: An `impl` block for a Neon class contains two or more methods named `new` (e.g. overloads or a private helper named `new`) — Rust has no overloading, so both are seen as constructors.
Common situations: Trying to emulate constructor overloading with multiple `new` fns of different parameter lists; a helper function accidentally named `new`; merge conflicts that duplicate the constructor block.
Related errors
- Constructor cannot have a `self` receiver
- Expected a context argument. Try removing the `context`…
- Context must be a `&mut` reference.
- Expected an owned `Channel` instead of a context reference.
- Context is not available in async functions. Try a…
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/19d69cea095cdda0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon-macros/src/class/mod.rs:790
})
}
fn group_class_items(items: Vec<syn::ImplItem>) -> Result<ClassItems, syn::Error> {
let mut consts = Vec::new();
let mut fns = Vec::new();
let mut constructor = None;
let mut has_finalizer = false;
for item in items {
match item {
syn::ImplItem::Const(c) => consts.push(c),
syn::ImplItem::Fn(f) => {
// Check if the function is a constructor
if f.sig.ident == "new" {
if constructor.is_some() {
let span = syn::spanned::Spanned::span(&f);
let msg = "Only one `new` constructor is allowed in a class.";
return Err(syn::Error::new(span, msg));
}
constructor = Some(f);
continue; // Skip adding to fns
} else if f.sig.ident == "finalize" {
if has_finalizer {
let span = syn::spanned::Spanned::span(&f);
let msg = "Only one `finalize` method is allowed in a class.";
return Err(syn::Error::new(span, msg));
}
has_finalizer = true;
continue; // Skip adding to fns
}
fns.push(f)
}
_ => {
let span = syn::spanned::Spanned::span(&item);
let msg = "`neon::class` can only contain `const` and `fn` items.";
return Err(syn::Error::new(span, msg));View on GitHub (pinned to 38960e4381)