emilk/egui · error

Your app must implement `as_any_mut`, but it doesn't

Error message

Your app must implement `as_any_mut`, but it doesn't

What it means

`AppRunner::app_mut<ConcreteApp>` requires the enclosed `App` to expose `&mut dyn Any` via `App::as_any_mut`; the `expect` panics when `as_any_mut()` returns `None`. This happens when a custom `App` implementation does not implement `as_any_mut` (e.g. returns `None` from a default/no-op impl), making the downcast to the concrete type impossible. eframe uses this for inspection/inspection plugins and for callers wanting typed access to their app.

Source

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

            .entry(egui::ViewportId::ROOT)
            .or_default()
            .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);
        }

View on GitHub (pinned to 441971a776)

Solutions

  1. Implement `as_any_mut` correctly: `fn as_any_mut(&mut self) -> Option<&mut dyn Any> { Some(self) }`.
  2. Verify the generic parameter matches the actual concrete app type (the second `expect` in the same function guards against type mismatch).
  3. Remove any manual `None` return or `panic!` stub left in `as_any_mut`.
  4. If you only need read access, use `app()`/`as_any` analogues — but still implement the method properly.

Example fix

// before
impl App for MyApp {
    fn as_any_mut(&mut self) -> Option<&mut dyn Any> { None }
}
// after
use std::any::Any;
impl App for MyApp {
    fn as_any_mut(&mut self) -> Option<&mut dyn Any> { Some(self) }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify typed access works before calling app_mut
fn can_downcast_to<A: App + 'static>(runner: &AppRunner) -> bool {
    runner.app.as_any().map_or(false, |a| a.is::<A>())
}

Type guard

fn as_my_app<'a>(app: &'a mut dyn App) -> Option<&'a mut MyApp> {
    app.as_any_mut()?.downcast_mut::<MyApp>()
}

Try / catch

// eframe panics rather than returning Result, so guard first:
if let Some(my_app) = runner.app.as_any_mut().and_then(|a| a.downcast_mut::<MyApp>()) {
    my_app.do_mutate();
}

Prevention

When it happens

Trigger: Calling `runner.app_mut::<MyApp>()` (directly or via eframe features like inspection) when `MyApp`'s `as_any_mut` returns `None` because it was not implemented or was implemented to return `None`.

Common situations: Implementing `App` with an empty/`unimplemented!`-style `as_any_mut` stub; copying an old example where the trait method was optional; mixing trait objects so the downcast target type doesn't match the real concrete type.

Related errors


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