a-b-street/abstreet · error
Input is missing geo_code
Error message
Input is missing geo_code: {:?} What it means
parse_zones converts every polygon in a zones GeoJSON into a map zone keyed by the 'geo_code' tag. When any feature's properties lack the geo_code key, it bails, printing the feature's tags so the offending record can be identified.
Solutions
- Rename the source property to 'geo_code' in the GeoJSON (e.g. jq '.features[].properties.geo_code = .features[].properties.GSS_CODE').
- Regenerate the zones file with a preprocessing script that sets geo_code on every feature.
- If you control the code, fall back to alternative tag names (tags.get("geo_code").or_else(|| tags.get("code"))).
Example fix
// before
{"properties": {"GSS_CODE": "E08000025"}}
// after
{"properties": {"geo_code": "E08000025"}} Defensive patterns
Strategy: validation
Validate before calling
let gj: serde_json::Value = serde_json::from_slice(&bytes)?;
for f in gj["features"].as_array().unwrap() {
if f["properties"]["geo_code"].is_null() {
bail!("feature missing geo_code: {:?}", f["properties"]);
}
} Type guard
fn has_geo_code(props: &serde_json::Map<String, serde_json::Value>) -> bool {
props.get("geo_code").map_or(false, |v| v.is_string())
} Try / catch
match parse_zones(path, gps_bounds, require_in_bounds) {
Ok(zones) => zones,
Err(e) if e.to_string().starts_with("Input is missing geo_code") => {
eprintln!("Rename the zone ID property to geo_code: {e}");
return Err(e);
}
Err(e) => return Err(e),
} Prevention
- Normalize official boundary files with a preprocessing step that sets geo_code
- Keep a documented schema for zone inputs and validate on ingest
- Check property names with jq '.features[0].properties | keys' before importing
When it happens
Trigger: Calling uk.rs generate_scenario with a zones input file whose features carry a different property name (e.g. 'code', 'GSS_CODE', 'id') instead of 'geo_code', or a feature with empty properties.
Common situations: Using zone shapefiles/GeoJSON from an official source (e.g. ONS boundaries) whose property schema doesn't match the pipeline's expected 'geo_code' key; hand-built GeoJSON missing properties.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- study area geojson has
- Unexpected geojson
- Expected exactly 1 feature
- doesn't have a column called Longitude, Latitude, or…
- Your clipboard doesn't seem to have GeoJSON. Got
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/4bd5e7b2c9cb05e7.
Report an issue: GitHub.
Appendix: source
Thrown at importer/src/uk.rs:201
#[serde(rename = "Driving a car or van")]
num_drivers: usize,
#[serde(rename = "Bicycle")]
num_bikers: usize,
#[serde(rename = "On foot")]
num_pedestrians: usize,
}
// Transforms all zones into the map's coordinate space, no matter how far out-of-bounds they are.
fn parse_zones(gps_bounds: &GPSBounds, path: String) -> Result<HashMap<String, Polygon>> {
let mut zones = HashMap::new();
let require_in_bounds = false;
for (polygon, tags) in
Polygon::from_geojson_bytes(&abstio::slurp_file(path)?, gps_bounds, require_in_bounds)?
{
if let Some(code) = tags.get("geo_code") {
zones.insert(code.to_string(), polygon);
} else {
bail!("Input is missing geo_code: {:?}", tags);
}
}
Ok(zones)
}
fn load_study_area(map: &Map) -> Result<Polygon> {
let require_in_bounds = true;
let mut list = Polygon::from_geojson_bytes(
&abstio::slurp_file(abstio::path(format!(
"system/study_areas/{}.geojson",
map.get_name().city.city.replace("_", "-")
)))?,
map.get_gps_bounds(),
require_in_bounds,
)?;
if list.len() != 1 {
bail!("study area geojson has {} polygons", list.len());
}View on GitHub (pinned to 0964f29315)