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
- Inspect the code path between the component push and line 418 and restore the invariant that every processed cell belongs to a pushed component
- Replace the expect with explicit handling: if `last_mut()` is None, start a new component and push the cell into it
- Add a debug_assert right after the push so regressions surface closer to the cause
- 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
- After refactoring the component-allocation flow, run the flowing_surface_* tests
- debug_assert!(!flowing_cells.is_empty()) immediately after the push
- Keep the push and the last_mut() use adjacent in the same branch
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
- scrap metal list is non-empty
- glass color array is non-empty
- Invalid id
- Failed to create tile XZBBox
- bundled font atlas is valid
AI-assisted analysis of louis-e/arnis@34048924d9 (2026-09-03).
Data as JSON: /api/errors/c45f4bf290767823.
Report an issue: GitHub.