MacOS crash #2

Open
opened 2026-07-27 21:50:15 +00:00 by jeroen · 3 comments
Owner

We got a MacOS crash. Can you verify whether it is still an issue? Seems to be egui-related. We don't have further debug logs. Is this enough to verify?

By closing the Mac, ViperFox crashed:
thread 'Compute Task Pool (3)' (2104571) panicked at crates/viberfox/src/systems/egui_manager.rs:146:14:
ui_mut() called outside an egui frame
Encountered a panic in system `<Enable the debug feature to see the name>`!
We got a MacOS crash. Can you verify whether it is still an issue? Seems to be egui-related. We don't have further debug logs. Is this enough to verify? ``` By closing the Mac, ViperFox crashed: thread 'Compute Task Pool (3)' (2104571) panicked at crates/viberfox/src/systems/egui_manager.rs:146:14: ui_mut() called outside an egui frame Encountered a panic in system `<Enable the debug feature to see the name>`! ```
Collaborator

🤖 Picked this up — working on branch issue-2 in a fresh worktree on the VPS. I'll report back here when I'm done.

🤖 Picked this up — working on branch `issue-2` in a fresh worktree on the VPS. I'll report back here when I'm done.
Collaborator

🤖 Picked this up — working on branch issue-2 in a fresh worktree on the VPS. I'll report back here when I'm done.

Label an issue agent:skip to keep me off it.

🤖 Picked this up — working on branch `issue-2` in a fresh worktree on the VPS. I'll report back here when I'm done. <sub>Label an issue `agent:skip` to keep me off it.</sub>
Collaborator

Pushed issue-2 (one commit, 1eb2b0c). PR link: https://code.garage44.eu/jeroen/viberfox/compare/main...issue-2

Yes, it was still an issue — and it reproduces without a Mac

The stack was enough. ui_mut() can only panic when root_ui is None during Update, and the only thing that leaves it None is begin_egui_frame bailing out early:

let Some((physical, scale)) = egui_surface(&windows, headless.as_ref()) else {
    return;   // <- root_ui stays None, Update runs anyway
};

That happens on any frame with no Window entity and no HeadlessSurface. Bevy's close_when_requested marks the window on one Last and despawns it on the next, so the shutdown sequence puts whole frames through the schedule with zero windows in the world. Every panel system then hits the expect in parallel — which is exactly the reported shape: an unnamed system, on a compute-pool thread.

What changed

crates/viberfox/src/systems/egui_manager.rs only:

  • begin_egui_frame now always begins the pass and builds the root Ui, laying out against the last surface it saw when there is none. Cost is one tessellated frame that is never presented.
  • end_egui_frame loses its matching early return — an unbalanced begin_pass would leave the pass open and nest the next one inside it (a second latent bug on the same path).
  • The surface (physical px + scale) now lives on EguiManager as one field, and a zero/NaN scale factor is sanitised on the way in — it divides every viewport size, and a display disappearing under us is precisely when a bad one would show up.

I considered gating the ~8 UI systems with a run_if instead and rejected it: it spreads the invariant across main.rs and silently drops the UI the day someone adds a panel and forgets the condition.

Verification

  • cargo check -p viberfox — clean (one pre-existing history_open dead-code warning).
  • cargo test -p viberfox --bin viberfox164 passed, including three new tests in egui_manager.
  • The proof it's the right bug: I temporarily put the old early return back and re-ran. Two of the new tests fail with the issue's message verbatim, on Compute Task Pool, followed by Encountered a panic in system <Enable the debug feature to see the name>!. With the fix they pass. The main test drives a real App through a WindowCloseRequested and asserts the window is actually gone, so it can't quietly stop testing the case it's about.

Two things for you

  1. systems::transit_live::tests::board_texture_renders_text fails in this container — it saves a preview to <repo>/target/board_preview.png, and my CARGO_TARGET_DIR is elsewhere so that directory doesn't exist. mkdir target and it passes. Pre-existing, untouched by this change, but it will bite any worktree or CI job with a redirected target dir.
  2. The macOS trigger is inferred, not observed. The panic proves a windowless frame occurred; shutdown is the mechanism I can point at and test. If the lid-close path produces one some other way, this fix still covers it — it no longer depends on knowing which event caused it. No visual check was possible here (no GPU), but this touches only frame lifecycle, not drawing; a normal run on a workstation should look identical.

Branch issue-2 (pushed)

1eb2b0c fix(ui): begin an egui pass on every frame, window or not

🤖 viberfox issue agent on the VPS — 49 turns · $3.87 · session bfbd9a42-ca8b-4bf9-bfe8-f74bb7bd0b01. Reply with @agent to continue this session.

Pushed `issue-2` (one commit, `1eb2b0c`). PR link: https://code.garage44.eu/jeroen/viberfox/compare/main...issue-2 ## Yes, it was still an issue — and it reproduces without a Mac The stack was enough. `ui_mut()` can only panic when `root_ui` is `None` during `Update`, and the only thing that leaves it `None` is `begin_egui_frame` bailing out early: ```rust let Some((physical, scale)) = egui_surface(&windows, headless.as_ref()) else { return; // <- root_ui stays None, Update runs anyway }; ``` That happens on any frame with no `Window` entity and no `HeadlessSurface`. Bevy's `close_when_requested` marks the window on one `Last` and despawns it on the *next*, so the shutdown sequence puts whole frames through the schedule with zero windows in the world. Every panel system then hits the `expect` in parallel — which is exactly the reported shape: an unnamed system, on a compute-pool thread. ## What changed `crates/viberfox/src/systems/egui_manager.rs` only: - `begin_egui_frame` now always begins the pass and builds the root `Ui`, laying out against the last surface it saw when there is none. Cost is one tessellated frame that is never presented. - `end_egui_frame` loses its matching early return — an unbalanced `begin_pass` would leave the pass open and nest the next one inside it (a second latent bug on the same path). - The surface (physical px + scale) now lives on `EguiManager` as one field, and a zero/NaN scale factor is sanitised on the way in — it divides every viewport size, and a display disappearing under us is precisely when a bad one would show up. I considered gating the ~8 UI systems with a `run_if` instead and rejected it: it spreads the invariant across `main.rs` and silently drops the UI the day someone adds a panel and forgets the condition. ## Verification - `cargo check -p viberfox` — clean (one pre-existing `history_open` dead-code warning). - `cargo test -p viberfox --bin viberfox` — **164 passed**, including three new tests in `egui_manager`. - The proof it's the right bug: I temporarily put the old early return back and re-ran. Two of the new tests fail with the issue's message verbatim, on `Compute Task Pool`, followed by `Encountered a panic in system <Enable the debug feature to see the name>!`. With the fix they pass. The main test drives a real `App` through a `WindowCloseRequested` and asserts the window is actually gone, so it can't quietly stop testing the case it's about. ## Two things for you 1. **`systems::transit_live::tests::board_texture_renders_text` fails in this container** — it saves a preview to `<repo>/target/board_preview.png`, and my `CARGO_TARGET_DIR` is elsewhere so that directory doesn't exist. `mkdir target` and it passes. Pre-existing, untouched by this change, but it will bite any worktree or CI job with a redirected target dir. 2. **The macOS trigger is inferred, not observed.** The panic proves a windowless frame occurred; shutdown is the mechanism I can point at and test. If the lid-close path produces one some *other* way, this fix still covers it — it no longer depends on knowing which event caused it. No visual check was possible here (no GPU), but this touches only frame lifecycle, not drawing; a normal run on a workstation should look identical. **Branch** `issue-2` (pushed) ``` 1eb2b0c fix(ui): begin an egui pass on every frame, window or not ``` --- <sub>🤖 viberfox issue agent on the VPS — 49 turns · $3.87 · session `bfbd9a42-ca8b-4bf9-bfe8-f74bb7bd0b01`. Reply with **@agent** to continue this session.</sub>
viberfox-agent removed this from the Viberfox project 2026-07-30 08:38:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeroen/cartopolis#2
No description provided.