neon-bindings/neon · error

Only one `finalize` method is allowed in a class.

Error message

Only one `finalize` method is allowed in a class.

What it means

The neon #[class] macro detects duplicate `finalize` methods when grouping class items. `finalize` implements Drop for the exported class, so at most one can exist; a second triggers this compile-time error at the span of the offending method.

Solutions

  1. Delete or rename one of the duplicate `finalize` methods so only one remains per class
  2. Merge the body of the two finalize methods into a single implementation
  3. If the second method is unrelated logic, rename it (e.g. `cleanup`) so the macro no longer treats it as a finalizer

Example fix

// before
impl_finalize!(Greet);
impl Class for Greet {
    fn finalize<'a>(&mut self, cx: &'a mut TaskContext<'a>) {}
    fn finalize<'a>(&mut self, cx: &'a mut TaskContext<'a>) {} // duplicate
}
// after
impl Class for Greet {
    fn finalize<'a>(&mut self, cx: &'a mut TaskContext<'a>) {
        // single merged finalizer body
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// before compiling, ensure a single finalize per class impl
fn validate_single_finalize(items: &[&str]) -> Result<(), String> {
    let count = items.iter().filter(|i| i.trim().starts_with("fn finalize")).count();
    if count > 1 { Err(format!("{count} finalize methods found; only one allowed")) } else { Ok(()) }
}

Prevention

When it happens

Trigger: Declaring two `fn finalize(self)` (or similar finalizer-named) methods inside a single `#[neon] impl` block of a class annotated with #[class].

Common situations: Refactoring that leaves an old finalizer in place while adding a new one; merge conflicts that keep both variants; copy-pasting a finalize method between impl blocks without removing the original; an associated fn accidentally named `finalize`.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

    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));
            }
        }
    }

    Ok(ClassItems {
        consts,
        fns,
        constructor,

View on GitHub (pinned to 38960e4381)