neon-bindings/neon · critical
Node-API symbol has not been loaded
Error message
Node-API symbol has not been loaded
What it means
Neon's generated `sys::bindings` module declares stub `extern "C"` functions for every Node-API symbol; each stub panics with this message when called. The real symbols are only wired up at module load time by the generated registration code. Hitting a stub means the addon was loaded without its Node-API symbol table being initialized — the function you called was never resolved.
Solutions
- Load the addon through Node (require/import the built `.node` file) so Neon's symbol registration runs; don't call Neon code from a standalone binary or plain unit test — use integration tests via Node instead.
- Enable the required cargo feature (e.g. `neon = { version = "0.10", features = ["napi-8"] }`) for the Node-API functions you use, then rebuild.
- Ensure `neon::setup()`/generated registration is invoked exactly once at module load and wasn't removed or cfg'd out.
- Rebuild the addon after changing neon versions so the generated symbol table matches the linked runtime.
Example fix
// before
// Cargo.toml
neon = "0.10" // missing feature
// code uses napi-8 APIs -> symbol stub panics
// after
// Cargo.toml
neon = { version = "0.10", features = ["napi-8"] } Defensive patterns
Strategy: validation
Validate before calling
// verify the addon loads through Node before exercising APIs
node -e "require('./index.node'); console.log('symbols loaded')" Prevention
- Only call Neon code from within a Node-loaded native module — use Node-run integration tests, not cargo unit tests
- Enable the napi-X cargo feature matching the APIs you call
- Keep neon registration code intact and called once at load
- Rebuild the addon whenever neon or feature flags change
When it happens
Trigger: Calling a Neon API whose backing Node-API symbol was never registered — commonly because the binary was built/run as a plain executable or test instead of being loaded by Node as a native module; using a symbol gated behind a newer `napi-X` feature flag that wasn't enabled; custom `#[node_api]`/sys usage before `init` ran.
Common situations: Running Neon code in `cargo test` unit tests or a `main.rs` binary where the Node module never loads; forgetting the `napi-6`/`napi-8` cargo feature while calling an API that requires it; linking the addon into a non-Node host that skips Neon's registration step.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- class must be implemented for a type name
- try_catch: unexpected Err(Throw) when VM is not in a…
- The `neon::main` macro must only be used once
- Attempted to dereference a `neon::handle::Root` from the…
- Must call `into_inner` or `drop` on `neon::handle::Root`
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/802852e93b6d1d7f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon/src/sys/bindings/mod.rs:123
/// pub(crate) unsafe fn get_undefined(env: Env, result: *mut Value) -> Status {
/// (NAPI.get_undefined)(env, result)
/// }
/// ```
macro_rules! generate {
(#[$extern_attr:meta] extern "C" {
$($(#[$attr:meta])? fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+
}) => {
struct Napi {
$(
$name: unsafe extern "C" fn(
$($param: $ptype,)*
)$( -> $rtype)*,
)*
}
#[inline(never)]
fn panic_load<T>() -> T {
panic!("Node-API symbol has not been loaded")
}
static mut NAPI: Napi = {
$(
unsafe extern "C" fn $name($(_: $ptype,)*)$( -> $rtype)* {
panic_load()
}
)*
Napi {
$(
$name,
)*
}
};
pub(super) unsafe fn load(host: &libloading::Library) {
let print_warn = |err| eprintln!("WARN: {}", err);View on GitHub (pinned to 38960e4381)