emilk/egui · critical

Single-use AppCreator has unexpectedly already been taken

Error message

Single-use AppCreator has unexpectedly already been taken

What it means

eframe stores your AppCreator closure in an Option and consumes it exactly once with core::mem::take when the root viewport is first created inside the event loop. This expect fires when the creation path is entered a second time after the closure was already taken: the event loop delivered Resumed/init twice (classic Android suspend/resume lifecycle) or the runner state was re-entered after a restart.

Source

Thrown at crates/eframe/src/native/glow_integration.rs:343

                ..
            } = viewport
            {
                egui_winit.init_accesskit(event_loop, window, event_loop_proxy);
            }
        }

        if self
            .native_options
            .viewport
            .mouse_passthrough
            .unwrap_or(false)
            && let Err(err) = glutin.window(ViewportId::ROOT).set_cursor_hittest(false)
        {
            log::warn!("set_cursor_hittest(false) failed: {err}");
        }

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

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

        let app: Box<dyn 'app + App> = {
            // Use latest raw_window_handle for eframe compatibility
            use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};

            let gl_config = glutin.gl_config.clone();
            let get_proc_address = move |addr: &_| gl_config.display().get_proc_address(addr);
            let window = glutin.window(ViewportId::ROOT);
            let cc = CreationContext {
                egui_ctx: integration.egui_ctx.clone(),
                integration_info: integration.frame.info().clone(),
                storage: integration.frame.storage(),
                gl: Some(gl),
                get_proc_address: Some(Arc::new(get_proc_address)),
                #[cfg(feature = "wgpu_no_default_features")]
                wgpu_render_state: None,

View on GitHub (pinned to d802a982ce)

Solutions

  1. Update eframe to the latest patch release (Android lifecycle double-resume handling has been fixed and refactored over time).
  2. Ensure run_native / the event loop is started exactly once per process.
  3. On Android, treat Activity recreation as a fresh process: recreate the runner state instead of re-entering the old one.
  4. If reproducible on current eframe, report with the backtrace and lifecycle events seen.
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Entering the Glow backend's root-window creation twice in one process: duplicate winit Resumed events on Android, re-running the event loop on a reused runner, or a winit/eframe version mismatch that re-delivers initialization events.

Common situations: Android apps where the Activity is suspended and recreated (second Resumed event); upgrading winit across event-loop API changes; embedding eframe in a host framework that restarts the GUI loop in the same process.

Related errors


AI-assisted analysis of emilk/egui@d802a982ce (2026-08-16). Data as JSON: /api/errors/934fafe0da2d79af. Report an issue: GitHub.