emilk/egui · error

We should always find the root

Error message

We should always find the root

What it means

egui's find_menu_root walks the Ui stack to locate the root of a menu/popup hierarchy and expects at least one ancestor to be a root Ui, a Popup/Menu, or carry the MenuConfig tag. The panic fires when a menu-related query is issued from a Ui whose ancestor stack contains no such root, meaning the code ran outside any initialized menu/popup context. It is an internal invariant of the menu system: every Ui used with menu APIs must descend from a menu root.

Solutions

  1. Ensure menu code runs inside a normal egui pass (inside Context::run / the eframe update callback) with a valid Ui from ctx or a window
  2. If using a custom Ui, wrap it in an area/window so a root Ui exists in the stack, or tag it with MenuConfig::MENU_CONFIG_TAG
  3. Verify the Ui passed to menu APIs was not captured from an earlier frame or created before any pass started
  4. Check for upgrade changes to crates/egui/src/containers/menu.rs root detection and adapt code to the new MenuConfig API

Example fix

// before
let menu_root = MenuConfig::default().find(ui); // ui captured from a previous frame
// after
ctx.run(ctx.input(|i| i.time), |ctx| {
    let ui = &mut egui::CentralPanel::default().show(ctx, |ui| {
        MenuConfig::default().find(ui)
    }).inner;
});
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust: only query menu state from a live pass Ui
if ctx.is_using_pointer() && std::ptr::eq(ui.ctx(), &ctx) {
    let root = MenuConfig::default().find(ui);
}

Type guard

fn has_menu_root(ui: &egui::Ui) -> bool {
    ui.ctx().find_menu_root_checked().is_some()
}

Prevention

When it happens

Trigger: Calling menu APIs (find/find_menu_root/from_ui) on a Ui created outside a Context::run/BeginPass frame, or on a Ui detached from any window/area whose stack is not marked as root, popup, menu, or tagged with MenuConfig::MENU_CONFIG_TAG.

Common situations: Showing a context menu during a custom repaint outside egui's frame loop; using a Ui captured from a previous frame; building menus in a deferred/threads context where the Ui stack was never pushed; version changes where the root-detection predicate became stricter.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at crates/egui/src/containers/menu.rs:42

pub fn menu_style(style: &mut Style) {
    style.spacing.button_padding = vec2(2.0, 0.0);
    style.visuals.widgets.active.bg_stroke = Stroke::NONE;
    style.visuals.widgets.open.bg_stroke = Stroke::NONE;
    style.visuals.widgets.hovered.bg_stroke = Stroke::NONE;
    style.visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
    style.visuals.widgets.inactive.bg_stroke = Stroke::NONE;
}

/// Find the root [`UiStack`] of the menu.
pub fn find_menu_root(ui: &Ui) -> &UiStack {
    ui.stack()
        .iter()
        .find(|stack| {
            stack.is_root_ui()
                || [Some(UiKind::Popup), Some(UiKind::Menu)].contains(&stack.kind())
                || stack.info.tags.contains(MenuConfig::MENU_CONFIG_TAG)
        })
        .expect("We should always find the root")
}

/// Is this Ui part of a menu?
///
/// Returns `false` if this is a menu bar.
/// Should be used to determine if we should show a menu button or submenu button.
pub fn is_in_menu(ui: &Ui) -> bool {
    for stack in ui.stack().iter() {
        if let Some(config) = stack
            .info
            .tags
            .get_downcast::<MenuConfig>(MenuConfig::MENU_CONFIG_TAG)
        {
            return !config.bar;
        }
        if [Some(UiKind::Popup), Some(UiKind::Menu)].contains(&stack.kind()) {
            return true;
        }

View on GitHub (pinned to 8fb03c9ae9)