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

  1. 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.
  2. Break initialization cycles between globals by initializing dependencies explicitly before entering Neon init.
  3. Avoid calling exported Neon functions from init-time hooks (loggers, signal handlers, atexit).
  4. 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

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


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)