emilk/egui · error

Context::root_ui was called while the root Ui was already…

Error message

Context::root_ui was called while the root Ui was already borrowed. You are probably calling it from within `Context::run_ui` or `Context::root_ui`. Use the `&mut Ui` you were given instead.

What it means

egui's Context only allows one borrow of the root Ui at a time. `Context::root_ui()` panics when the root Ui is already checked out — typically because you are inside a `Context::run_ui` (or a previous `root_ui`) closure that already handed you an `&mut Ui`. The library deliberately panics (RootUi::Borrowed) instead of returning an error because this is an API-usage invariant violation.

Solutions

  1. Use the `&mut Ui` already passed to your closure (from `run_ui` or the outer `root_ui` call) instead of calling `ctx.root_ui()` again
  2. Restructure so nested widget code takes `ui: &mut Ui` as a parameter rather than re-acquiring the root Ui from the Context
  3. Ensure the previous `root_ui()`/`run_ui` call has fully returned (drop its guard) before calling `root_ui` again on the same context
  4. Split code that needs a fresh Ui into a separate pass outside the active borrow

Example fix

// before
ctx.run_ui(ctx.screen_rect(), |ctx| {
    ctx.root_ui().add(egui::Label::new("hello")); // panics: root Ui already borrowed
});
// after
ctx.run_ui(ctx.screen_rect(), |ctx| {
    let mut ui = ctx.root_ui(); // borrow once
    ui.add(egui::Label::new("hello"));
});
// or inside a run_ui closure with a provided ui:
ctx.run_ui(ctx.screen_rect(), |_ctx, ui| {
    ui.add(egui::Label::new("hello")); // use the given &mut Ui
});
Defensive patterns

Strategy: type-guard

Validate before calling

// Track whether a root Ui borrow is already active in this call path.
// Prefer passing `&mut Ui` down instead of calling ctx.root_ui() in helpers:
fn draw_panel(ui: &mut Ui) {
    // use ui, never ctx.root_ui() here
    ui.label("panel");
}
// Call it as: draw_panel(&mut ui) inside run_ui/root_ui scope.

Type guard

fn can_call_root_ui(borrow_active: bool) -> bool { !borrow_active } // only call ctx.root_ui() when no enclosing run_ui/root_ui scope is active

Prevention

When it happens

Trigger: Calling `ctx.root_ui(...)` while a previous `root_ui` call is still active, or calling it from inside the closure passed to `Context::run_ui` / `run_ui`-style methods where an `&mut Ui` was already provided. Also caused by holding the `&mut Ui` from a first `root_ui()` call while making a nested second call.

Common situations: Migrating code from older egui versions that allowed `ctx.root_ui()` anywhere (e.g. inside `Update` callbacks or widget code) to newer versions with a borrow-tracked root Ui; wrapping legacy immediate-mode drawing code in a `run_ui` closure and then calling `ctx.root_ui()` inside it; nesting `root_ui` calls in helper functions that each grab the root Ui.

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-19). Data as JSON: /api/errors/74bcf59a4c3e3bde. Report an issue: GitHub.

Appendix: source

Thrown at crates/egui/src/context.rs:1000

    /// Take the root [`Ui`] out of the viewport state, creating it if needed.
    ///
    /// The [`Ui`] must be put back with [`RootUi::Available`] when done,
    /// so that the next call to [`Self::root_ui`] can find it.
    fn take_root_ui(&self) -> Ui {
        let root_ui =
            self.write(|ctx| core::mem::replace(&mut ctx.viewport().root_ui, RootUi::Borrowed));
        match root_ui {
            RootUi::Available(root_ui) => root_ui,
            RootUi::Pending => Ui::new(
                self.clone(),
                self.viewport_id().root_ui_id(),
                UiBuilder::new()
                    .layer_id(LayerId::background())
                    .max_rect(self.viewport_rect()),
            ),
            RootUi::Borrowed => {
                panic!(
                    "Context::root_ui was called while the root Ui was already borrowed. \
                     You are probably calling it from within `Context::run_ui` or `Context::root_ui`. \
                     Use the `&mut Ui` you were given instead."
                );
            }
        }
    }

    /// Drop the root [`Ui`] of the current pass, if any,
    /// remembering how much of it was used.
    ///
    /// Called at the end of each pass.
    fn finish_root_ui(&self) {
        let root_ui = self.write(|ctx| core::mem::take(&mut ctx.viewport().root_ui));
        match root_ui {
            RootUi::Pending => {}
            RootUi::Available(root_ui) => {
                self.pass_state_mut(|state| {

View on GitHub (pinned to 8fb03c9ae9)