a-b-street/abstreet · warning · anyhow::Error

multipolygon was unexpectedly empty

Error message

multipolygon was unexpectedly empty

What it means

While iterating census area features from the flatgeobuf file, each MultiPolygon geometry is converted and only its first polygon is kept. If the MultiPolygon has zero polygons (an empty geometry), .first() is None and this error is thrown. It signals a malformed/degenerate census feature rather than a caller mistake.

Solutions

  1. Skip empty MultiPolygon features with a warning instead of failing the whole import
  2. Regenerate/re-download population_areas.fgb from the source
  3. Validate geometry non-emptiness in the import handbook pipeline before publishing the fgb

Example fix

// before
let geo_polygon = multi_poly.0.first().ok_or_else(|| anyhow!("multipolygon was unexpectedly empty"))?;
// after
let geo_polygon = match multi_poly.0.first() {
    Some(p) => p,
    None => {
        warn!("census area {:?} has an empty multipolygon; skipping", props);
        continue;
    }
};
Defensive patterns

Strategy: fallback

Validate before calling

// treat empty multipolygons as skippable at the decode site
if let geo::Geometry::MultiPolygon(mp) = &geom_result {
    if mp.0.is_empty() { /* skip feature */ }
}

Type guard

fn non_empty_multipolygon(g: &geo::Geometry<f64>) -> Option<&geo::MultiPolygon<f64>> {
    match g { geo::Geometry::MultiPolygon(mp) if !mp.0.is_empty() => Some(mp), _ => None }
}

Try / catch

match fetch_all_for_map(map).await { Err(e) if e.to_string().contains("unexpectedly empty") => warn!("some census features were empty; results partial"), r => r }

Prevention

When it happens

Trigger: fetch_all_for_map reads a feature whose decoded geometry yields geo::Geometry::MultiPolygon with an empty Vec — an empty census area polygon in population_areas.fgb, or a failed/partial geometry decode by GeoWriter.

Common situations: Upstream changes to the census source producing empty polygons for some regions; querying the FGB at a bbox edge that clips features to nothing; stale or corrupted cached fgb data.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/ff31fb08ff9473ce. Report an issue: GitHub.

Appendix: source

Thrown at popdat/src/import_census.rs:60

            if !props.contains_key("population") {
                warn!("skipping feature with missing population");
                continue;
            }
            let population: usize = props["population"].parse()?;
            let geometry = match feature.geometry() {
                Some(g) => g,
                None => {
                    warn!("skipping feature with missing geometry");
                    continue;
                }
            };
            let mut geo = GeoWriter::new();
            geometry.process(&mut geo, flatgeobuf::GeometryType::MultiPolygon)?;
            if let Some(geo::Geometry::MultiPolygon(multi_poly)) = geo.take_geometry() {
                let geo_polygon = multi_poly
                    .0
                    .first()
                    .ok_or_else(|| anyhow!("multipolygon was unexpectedly empty"))?;
                if multi_poly.0.len() > 1 {
                    warn!(
                        "dropping {} extra polygons from census area: {:?}",
                        multi_poly.0.len() - 1,
                        props
                    );
                }

                if !geo_polygon.intersects(&geo_map_area) {
                    debug!(
                        "skipping polygon outside of map area. polygon: {:?}, map_area: {:?}",
                        geo_polygon, geo_map_area
                    );
                    continue;
                }

                let mut polygon = geo_polygon.clone();
                polygon.map_coords_in_place(|c| geom::LonLat::new(c.x, c.y).to_pt(bounds).into());

View on GitHub (pinned to 0964f29315)