louis-e/arnis · error

component pushed above

Error message

component pushed above

What it means

In `level_water_surfaces` (postprocess.rs:418), the code appends to the flowing-water component list with `last.push(...)` guarded by `.expect("component pushed above")` on `flowing_cells.last_mut()`. The invariant is that the cell being processed was just pushed to `flowing_cells` earlier in the same iteration, so `last_mut()` must succeed; the expect fires only if that invariant was broken (the push was skipped or moved).

Source

Thrown at src/elevation/postprocess.rs:418

                if iqr > max_flowing_iqr {
                    max_flowing_iqr = iqr;
                }
                flowing_cells.push(Vec::new());
                for &(cx, cy) in &component {
                    let orig = heights_snapshot[cy][cx];
                    if !orig.is_finite() {
                        continue;
                    }
                    let local_surface = local_water_median(
                        &heights_snapshot,
                        lc_grid,
                        cx,
                        cy,
                        LOCAL_SURFACE_RADIUS,
                        MIN_LOCAL_SAMPLES,
                    )
                    .unwrap_or(fallback_median);
                    let last = flowing_cells.last_mut().expect("component pushed above");
                    last.push((cx as u32, cy as u32, local_surface as f32));
                }
            } else {
                // ── Still water (lake / fjord / ocean) ─────────────────
                // Estimate a single surface for the whole component via
                // histogram mode (robust to both upper and lower tails),
                // then clamp by adjacent land p25 so the body can't sit
                // above its own shore (Arnis Baltic fjord case).
                still_components += 1;
                let raw_surface = if values.len() >= MIN_MODE_SAMPLES {
                    histogram_mode(&values, MODE_BIN_SIZE_M)
                } else {
                    fallback_median
                };
                let surface =
                    clamp_by_adjacent_land(raw_surface, &component, &heights_snapshot, lc_grid);

                for &(cx, cy) in &component {

View on GitHub (pinned to 34048924d9)

Solutions

  1. Inspect the code path between the component push and line 418 and restore the invariant that every processed cell belongs to a pushed component
  2. Replace the expect with explicit handling: if `last_mut()` is None, start a new component and push the cell into it
  3. Add a debug_assert right after the push so regressions surface closer to the cause
  4. Run the guarded tests (`flowing_surface_*` tests call this function) after any refactor of this pass

Example fix

// before
let last = flowing_cells.last_mut().expect("component pushed above");
last.push((cx as u32, cy as u32, local_surface as f32));
// after
let cell = (cx as u32, cy as u32, local_surface as f32);
match flowing_cells.last_mut() {
    Some(c) => c.push(cell),
    None => flowing_cells.push(vec![cell]),
}
Defensive patterns

Strategy: try-catch

Try / catch

// Use Option handling instead of expect at the mutation site:
if let Some(c) = flowing_cells.last_mut() { c.push(cell); } else { flowing_cells.push(vec![cell]); }

Prevention

When it happens

Trigger: A code change that makes the branch reachable without a prior `flowing_cells.push(...)` (e.g. refactoring component allocation, adding an early-continue after allocation, or merging branches); not reachable in an unmodified build.

Common situations: Regression after refactoring the water-surface leveling pass; changing the component-allocation logic so some cells are processed with no active component.

Related errors


AI-assisted analysis of louis-e/arnis@34048924d9 (2026-09-03). Data as JSON: /api/errors/c45f4bf290767823. Report an issue: GitHub.