emilk/egui · error

install_surface called before render_state initialization

Error message

install_surface called before render_state initialization

What it means

install_surface asserts self.render_state.is_some() before configuring a newly created surface, panicking with "install_surface called before render_state initialization" if the renderer's wgpu render state has not been set up yet. Render state (instance, adapter, device, queue) is normally created once up front; installing or recreating a surface before that initialization completes leaves the code without an adapter/device to query capabilities and configure against, so it panics.

Solutions

  1. Ensure the egui_wgpu painter/renderer is fully initialized (RenderState created) before calling recreate_surface/add_surface/install_surface — typically create the painter, then install surfaces.
  2. Defer surface installation until after first-frame initialization: create secondary-window surfaces in a later event-loop iteration once render_state exists.
  3. If render-state creation can fail, handle that case explicitly and skip surface recreation instead of proceeding with a None render_state.
  4. When writing custom viewport code, check painter.render_state before touching surfaces and route surface setup through the public painter APIs.

Example fix

// before: installing a surface before render state exists
let painter = egui_wgpu::ScreenDescriptorlessPainter::new(...); // render_state not yet created
painter.add_surface(surface, viewport_id, 0, 0, false); // panics
// after: initialize render state first
let painter = egui_wgpu::Painter::new(config);
painter.set_window(viewport_id, Some(window.clone()))?; // triggers RenderState init
// only then:
painter.add_surface(surface, viewport_id, width, height, transparent);
Defensive patterns

Strategy: validation

Validate before calling

// Rust: only install surfaces once render state is initialized
if painter.render_state().is_none() {
    painter.set_window(viewport_id, Some(window.clone()))?; // triggers init
}

Type guard

fn render_state_ready(painter: &egui_wgpu::Painter) -> bool {
    painter.render_state().is_some()
}

Prevention

When it happens

Trigger: Calling install_surface indirectly via recreate_surface or add_surface before RenderState has been created (e.g. attempting to add a viewport/surface during startup before egui_wgpu::Renderer / RenderState::new finished, or after render_state was taken/failed). Also triggered by calling these internals directly in custom viewport code that runs before set_up / renderer init.

Common situations: Custom multi-viewport code installing surfaces for secondary windows during app construction, before the first frame initialized the painter; async RenderState creation still pending when a window-creation callback fires; a failed/skipped render-state initialization (e.g. no compatible adapter) followed by surface teardown/recreate calls.

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@8fb03c9ae9 (2026-09-12). Data as JSON: /api/errors/99a8b598958b5559. Report an issue: GitHub.

Appendix: source

Thrown at crates/egui-wgpu/src/winit.rs:262

    /// Inserts a freshly created surface into [`Self::surfaces`] and configures it.
    ///
    /// Render state must already be initialised before calling this.
    // NOTE: The same assumption is already required by `resize_and_generate_depth_texture_view_and_msaa_view`.
    fn install_surface(
        &mut self,
        surface: wgpu::Surface<'static>,
        viewport_id: ViewportId,
        width: u32,
        height: u32,
        resizing: bool,
    ) {
        let alpha_mode = {
            // Panic: We use the same failure mode as `resize_and_generate_depth_texture_view_and_msaa_view`
            let render_state = self
                .render_state
                .as_ref()
                .expect("install_surface called before render_state initialization");
            if self.support_transparent_backbuffer {
                let supported_alpha_modes =
                    surface.get_capabilities(&render_state.adapter).alpha_modes;
                // Prefer pre multiplied over post multiplied!
                if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {
                    wgpu::CompositeAlphaMode::PreMultiplied
                } else if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
                {
                    wgpu::CompositeAlphaMode::PostMultiplied
                } else {
                    log::warn!(
                        "Transparent window was requested, but the active wgpu surface does not support a `CompositeAlphaMode` with transparency."
                    );
                    wgpu::CompositeAlphaMode::Auto
                }
            } else {
                wgpu::CompositeAlphaMode::Auto
            }

View on GitHub (pinned to 8fb03c9ae9)