neon-bindings/neon · error

The `neon::main` macro must only be used once

Error message

The `neon::main` macro must only be used once

What it means

The `#[neon::main]` macro registers a custom module-init entry point in a global registry (`crate::macro_internal::MAIN`). At runtime, `init` checks the registry and panics if more than one `#[neon::main]` function was registered, because a native module can only have a single entry point.

Solutions

  1. Keep exactly one function annotated with `#[neon::main]`; merge the bodies of duplicates into that single function.
  2. Gate alternative main functions with `#[cfg(...)]` so only one is compiled per feature set.
  3. Delete the legacy `register_module!`/old main when migrating to `#[neon::main]`.
  4. Search the crate (`grep -rn 'neon::main'`) to find all annotated functions before adding a new one.

Example fix

// before
#[neon::main]
fn main_a(mut cx: ModuleContext) -> NeonResult<()> { /* ... */ Ok(()) }
#[neon::main]
fn main_b(mut cx: ModuleContext) -> NeonResult<()> { /* ... */ Ok(()) }

// after
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
    main_a_impl(&mut cx)?;
    main_b_impl(&mut cx)?;
    Ok(())
}
Defensive patterns

Strategy: validation

Validate before calling

// CI check: fail if the crate declares more than one neon::main
grep -rn '#\[neon::main\]' src/ | wc -l  # must be <= 1

Prevention

When it happens

Trigger: Annotating two different functions with `#[neon::main]` in the same crate (including transitively via a macro or an imported helper macro); copying example code that already contains a `#[neon::main]` into a crate that already defines one.

Common situations: Merging branches where both added a `#[neon::main]` handler; enabling a feature flag (e.g. `#[cfg(feature = "tokio")]` variants) that pulls in a second main; mixing `neon::main` with an older `register_module!` setup retained in the codebase.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at crates/neon/src/context/internal.rs:65

}

pub trait ContextInternal<'cx>: Sized {
    fn cx(&self) -> &Cx<'cx>;
    fn cx_mut(&mut self) -> &mut Cx<'cx>;
    fn env(&self) -> Env {
        self.cx().env
    }
}

fn default_main(mut cx: ModuleContext) -> NeonResult<()> {
    #[cfg(all(feature = "napi-6", feature = "tokio-rt-multi-thread"))]
    crate::executor::tokio::init(&mut cx)?;
    crate::registered().export(&mut cx)
}

fn init(cx: ModuleContext) -> NeonResult<()> {
    if crate::macro_internal::MAIN.len() > 1 {
        panic!("The `neon::main` macro must only be used once");
    }

    if let Some(main) = crate::macro_internal::MAIN.first() {
        main(cx)
    } else {
        default_main(cx)
    }
}

#[no_mangle]
unsafe extern "C" fn napi_register_module_v1(env: *mut c_void, m: *mut c_void) -> *mut c_void {
    let env = env.cast();

    sys::setup(env);

    IS_RUNNING.with(|v| {
        *v.borrow_mut() = true;
    });

View on GitHub (pinned to 38960e4381)