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).

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.

Appendix: 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)