emilk/egui · error

app_mut got the wrong type of App

Error message

app_mut got the wrong type of App

What it means

AppRunner::app_mut downcasts the boxed App to a caller-chosen concrete type via App::as_any_mut. The .expect fires when the downcast_mut::<ConcreteApp>() returns None, i.e. the stored App instance is not actually of the requested type. The library panics because there is no way to return a typed reference safely when the caller names the wrong type.

Source

Thrown at crates/eframe/src/web/app_runner.rs:192

            .native_pixels_per_point = Some(super::native_pixels_per_point());
        runner.input.raw.system_theme = super::system_theme();

        Ok(runner)
    }

    pub fn egui_ctx(&self) -> &egui::Context {
        &self.egui_ctx
    }

    /// Get mutable access to the concrete [`App`] we enclose.
    ///
    /// This will panic if your app does not implement [`App::as_any_mut`].
    pub fn app_mut<ConcreteApp: 'static + App>(&mut self) -> &mut ConcreteApp {
        self.app
            .as_any_mut()
            .expect("Your app must implement `as_any_mut`, but it doesn't")
            .downcast_mut::<ConcreteApp>()
            .expect("app_mut got the wrong type of App")
    }

    pub fn auto_save_if_needed(&mut self) {
        let time_since_last_save = now_sec() - self.last_save_time;
        if time_since_last_save > self.app.auto_save_interval().as_secs_f64() {
            self.save();
        }
    }

    pub fn save(&mut self) {
        if self.app.persist_egui_memory() {
            super::storage::save_memory(&self.egui_ctx);
        }
        if let Some(storage) = self.frame.storage_mut() {
            self.app.save(storage);
        }
        self.last_save_time = now_sec();
    }

View on GitHub (pinned to 441971a776)

Solutions

  1. Make the generic parameter of app_mut exactly the concrete type the runner was constructed with (e.g. app_mut::<MyApp>()).
  2. Verify App::as_any_mut is implemented as `self` on the concrete app (#[inline] fn as_any_mut(&mut self) -> &mut dyn Any { self }), not on a wrapper.
  3. Search for other constructions of the runner to find which App type is actually stored.

Example fix

// before
let app = runner.app_mut::<OldApp>();
// after
let app = runner.app_mut::<MyApp>();
Defensive patterns

Strategy: type-guard

Validate before calling

fn app_is<T: 'static + App>(runner: &AppRunner) -> bool { runner.app().as_any().is::<T>() }

Type guard

fn as_concrete<T: 'static + App>(runner: &mut AppRunner) -> Option<&mut T> { runner.app_mut::<T>() }.ok()
// safer: check first: runner.app().as_any().downcast_ref::<T>().is_some()

Try / catch

// Rust panics; use std::panic::catch_unwind only at integration boundaries, otherwise ensure the concrete type via downcast_ref check before app_mut.

Prevention

When it happens

Trigger: Calling runner.app_mut::<MyApp>() where the runner was created with a different concrete App type (or a wrapper/box type that doesn't downcast to MyApp), typically because as_any_mut returns self but the generic parameter doesn't match the constructed app.

Common situations: Copy-pasting app_mut calls from examples into a project whose App type was renamed; running multiple apps and passing the wrong runner; wrapping the App in a newtype that forwards as_any_mut incorrectly; using app_mut in tests with a mock app of a different type.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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