emilk/egui · error

Single-use AppCreator has unexpectedly already been taken

Error message

Single-use AppCreator has unexpectedly already been taken

What it means

`app_creator` is a `Option<AppCreator>` consumed exactly once during startup: `init_run_state` does `core::mem::take(&mut self.app_creator)` and panics if it was already `None`. This enforces the single-use contract — the closure that builds your `App` must run exactly once when the first window/viewport initializes. Seeing this panic means `init_run_state` was entered twice (or the creator was taken elsewhere), which is an internal state machine violation.

Source

Thrown at crates/eframe/src/native/wgpu_integration.rs:314

        #[allow(clippy::allow_attributes, unused_mut)] // used for accesskit
        let mut egui_winit = egui_winit::State::new(
            egui_ctx.clone(),
            ViewportId::ROOT,
            event_loop,
            Some(window.scale_factor() as f32),
            event_loop.system_theme(),
            painter.max_texture_side(),
        );

        #[cfg(feature = "accesskit")]
        {
            let event_loop_proxy = self.repaint_proxy.lock().clone();
            egui_winit.init_accesskit(event_loop, &window, event_loop_proxy);
        }

        let app_creator = core::mem::take(&mut self.app_creator)
            .expect("Single-use AppCreator has unexpectedly already been taken");

        crate::maybe_attach_inspection_plugin(&egui_ctx, Some(self.app_name.clone()));

        let cc = CreationContext {
            egui_ctx: egui_ctx.clone(),
            integration_info: integration.frame.info().clone(),
            storage: integration.frame.storage(),
            #[cfg(feature = "glow")]
            gl: None,
            #[cfg(feature = "glow")]
            get_proc_address: None,
            wgpu_render_state,
            window: Some(Arc::clone(&window)),
            raw_display_handle: window.display_handle().map(|h| h.as_raw()),
            raw_window_handle: window.window_handle().map(|h| h.as_raw()),
        };
        let app = {
            profiling::scope!("user_app_creator");

View on GitHub (pinned to 441971a776)

Solutions

  1. Ensure `run_native`/`eframe::run_simple_native` is called once per process and the event loop is not restarted with reused state.
  2. Create a fresh `NativeOptions` and let eframe construct its own run state instead of reusing or caching integration state.
  3. In custom integrations, check that only one viewport initialization path calls `init_run_state`/consumes `app_creator`.
  4. If using the wasm/web backend with hot-reload, make sure state isn't carried across reloads into a second init.

Example fix

// before: reusing state across a manual event-loop restart
let mut state = RunState::new(options, app_creator);
// ... later, with the same state
state.init_run_state(&event_loop)?; // panics if already taken
// after: build a fresh run state each time you start an event loop
let mut state = RunState::new(fresh_options, app_creator);
state.init_run_state(&event_loop)?;
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the creator is still available before re-running init
fn ensure_creator(state: &RunState) -> Result<(), String> {
    if state.app_creator.is_none() {
        return Err("AppCreator already consumed; build a fresh RunState".into());
    }
    Ok(())
}

Prevention

When it happens

Trigger: Calling `init_run_state` twice on the same `RunState`/integration (e.g. re-initializing after the event loop restarted), or a viewport-instantiation path that runs while a previous initialization already consumed the `AppCreator`.

Common situations: Custom multi-viewport setups where the app-creation hook fires twice; embedding eframe and manually restarting the event loop with the same options/state; forks or patched integrations that call init paths directly.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of emilk/egui@441971a776 (2026-09-12). Data as JSON: /api/errors/f731f35737cbdb28. Report an issue: GitHub.