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
- 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
- Restructure so nested widget code takes `ui: &mut Ui` as a parameter rather than re-acquiring the root Ui from the Context
- Ensure the previous `root_ui()`/`run_ui` call has fully returned (drop its guard) before calling `root_ui` again on the same context
- 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
- Never call ctx.root_ui() inside a run_ui/root_ui closure; use the provided &mut Ui
- Pass `&mut Ui` down to helper functions instead of the Context
- Keep root_ui()/run_ui scopes short and non-overlapping; do not store the returned Ui beyond the closure
- Audit UI helper code when upgrading egui versions, since older APIs allowed unrestricted ctx.root_ui() calls
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
- Harness::ui_id is only available for harnesses built with a…
- Could not create Date
- Could not create Date
- {err}
- failed to get current context to resize surface
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)