emilk/egui · error

No fonts available until first call to Context::run()

Error message

No fonts available until first call to Context::run()

What it means

Context::fonts() gives read-write access to egui's Fonts via a closure, but fonts are only initialized during the first pass started by Context::run(). Before that, ctx.fonts is None and the expect panics. This is an initialization-order guard: font-dependent APIs are meaningless before the first frame.

Solutions

  1. Move the ctx.fonts() call into your frame callback (inside Context::run / eframe's update fn)
  2. Defer any text-metric-dependent setup until the first frame executes
  3. If you need fonts before the first frame, call Context::run (or begin_pass/end_pass) once beforehand
  4. In tests, use an initialized harness (e.g. run one pass) before asserting on fonts

Example fix

// before
let ctx = egui::Context::default();
let h = ctx.fonts(|f| f.layout_no_wrap("hi".into(), id, color)); // panics
// after
let ctx = egui::Context::default();
ctx.run(Instant::now(), |ctx| {
    let h = ctx.fonts(|f| f.layout_no_wrap("hi".into(), id, color));
});
Defensive patterns

Strategy: validation

Validate before calling

// Rust: guard before querying fonts
if !ctx.will_discard() && has_run_once(ctx) {
    ctx.fonts(|f| /* ... */);
}

Type guard

fn fonts_available(ctx: &egui::Context) -> bool {
    ctx.memory(|m| !m.areas.is_empty()) // implies at least one pass ran
}

Prevention

When it happens

Trigger: Calling ctx.fonts(...) (write access variant, context.rs:1163) before the first Context::run()/BeginPass call, e.g. during backend setup, in a constructor, or in a test that builds a Context directly.

Common situations: Measuring text or querying font layout in app initialization code before the eframe update loop starts; unit tests constructing Context::default() and immediately reading fonts; plugins reading fonts in an init hook.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

    ///
    /// This is swapped at the end of each pass.
    #[inline]
    pub(crate) fn prev_pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
        self.write(move |ctx| reader(&ctx.viewport().prev_pass))
    }

    /// Read-only access to [`Fonts`].
    ///
    /// Not valid until first call to [`Context::run_ui()`].
    /// That's because since we don't know the proper `pixels_per_point` until then.
    #[inline]
    pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
        self.write(move |ctx| {
            let pixels_per_point = ctx.pixels_per_point();
            reader(
                &ctx.fonts
                    .as_mut()
                    .expect("No fonts available until first call to Context::run()")
                    .with_pixels_per_point(pixels_per_point),
            )
        })
    }

    /// Read-write access to [`Fonts`].
    ///
    /// Not valid until first call to [`Context::run_ui()`].
    /// That's because since we don't know the proper `pixels_per_point` until then.
    #[inline]
    pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
        self.write(move |ctx| {
            let pixels_per_point = ctx.pixels_per_point();
            reader(
                &mut ctx
                    .fonts
                    .as_mut()
                    .expect("No fonts available until first call to Context::run()")

View on GitHub (pinned to 8fb03c9ae9)