neon-bindings/neon · error
attempt to reinitialize Local during initialization
Error message
attempt to reinitialize Local during initialization
What it means
`LocalCell::pre_init` initializes thread-local Neon state exactly once, using an `Uninit/Trying/Init` state machine. Panicking with this message means `pre_init` (or the initializing closure) was re-entered while the cell was still in the `Trying` state — i.e. the initialization function itself tried to read the same thread-local value, causing illegal recursive initialization.
Solutions
- Remove any access to Neon instance/thread-local state from inside the `pre_init` initializer closure; compute plain values first, store them, and only use Neon APIs afterwards.
- Break initialization cycles between globals by initializing dependencies explicitly before entering Neon init.
- Avoid calling exported Neon functions from init-time hooks (loggers, signal handlers, atexit).
- If triggered by a Neon upgrade, check the changelog for pre_init semantics and update custom `Instance`/instance-data code to the current API.
Example fix
// before
INSTANCE.pre_init(|cell| {
let cx = current_cx(); // reads the same LocalCell -> re-entrant init
cell.store(build_data(cx))
});
// after
let data = build_data_plain(); // no Neon thread-local access
INSTANCE.pre_init(|cell| cell.store(data)); Defensive patterns
Strategy: validation
Validate before calling
// audit init-time code: nothing inside pre_init closures may touch Neon APIs
fn assert_no_neon_in_init<F: FnOnce()>(f: F) { f(); } // review-only pattern Prevention
- Keep pre_init closures free of Neon context/thread-local access
- Initialize dependencies (loggers, globals) before Neon module init
- Don't call exported Neon functions from panic hooks or signal handlers
- After Neon upgrades, re-check custom instance-data initialization against the new API
When it happens
Trigger: The closure passed to `pre_init` (or anything it calls, directly or transitively) accesses the same `LocalCell` thread-local before initialization completes — e.g. instance-data setup code that calls back into Neon APIs which read instance state; re-entrant module initialization on the same thread.
Common situations: Custom `InstanceData`/singleton setup that calls Neon context APIs during initialization; logging or panic hooks that touch Neon thread-locals during init; cyclic initialization between two lazily-initialized globals.
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
- 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/f652d5154fa564d8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon/src/lifecycle.rs:100
/// Intermediate "dirty" state representing the middle of a `get_or_try_init` transaction.
Trying,
/// Fully initialized state.
Init(LocalCellValue),
}
impl LocalCell {
/// Establish the initial state at the beginning of the initialization protocol.
/// This method ensures that re-entrant initialization always panics (i.e. when
/// an existing `get_or_try_init` is in progress).
fn pre_init<F>(&mut self, f: F)
where
F: FnOnce() -> LocalCell,
{
match self {
LocalCell::Uninit => {
*self = f();
}
LocalCell::Trying => panic!("attempt to reinitialize Local during initialization"),
LocalCell::Init(_) => {}
}
}
pub(crate) fn get<'cx, 'a, C>(cx: &'a mut C, id: usize) -> Option<&'a mut LocalCellValue>
where
C: Context<'cx>,
{
let cell = InstanceData::locals(cx).get(id);
match cell {
LocalCell::Init(ref mut b) => Some(b),
_ => None,
}
}
pub(crate) fn get_or_init<'cx, 'a, C, F>(
cx: &'a mut C,
id: usize,View on GitHub (pinned to 38960e4381)