{"record":{"id":"61d9dd99287cfef7","repo":"a-b-street/abstreet","slug":"unexpected-geojson","errorCode":null,"errorMessage":"Unexpected geojson: {:?}","messagePattern":"Unexpected geojson: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"importer/src/pick_geofabrik.rs","lineNumber":42,"sourceCode":"        .min_by_key(|(mp, _)| mp.unsigned_area() as usize)\n        .unwrap();\n\n    // Contains some directory structure, like north-america/us/wyoming-latest.osm.pbf or\n    // asia/yemen-latest.osm.pbf\n    let basename = url\n        .strip_prefix(\"https://download.geofabrik.de/\")\n        .expect(\"Geofabrik URLs changed\");\n    let local = abstio::path_shared_input(format!(\"geofabrik/{basename}\"));\n\n    Ok((url, local))\n}\n\nfn load_boundary(path: String) -> Result<geo::Polygon> {\n    let gj: GeoJson = abstio::maybe_read_json(path, &mut Timer::throwaway())?;\n    let mut features = match gj {\n        GeoJson::Feature(feature) => vec![feature],\n        GeoJson::FeatureCollection(feature_collection) => feature_collection.features,\n        _ => bail!(\"Unexpected geojson: {:?}\", gj),\n    };\n    if features.len() != 1 {\n        bail!(\"Expected exactly 1 feature\");\n    }\n    let poly: geo::Polygon = features\n        .pop()\n        .unwrap()\n        .geometry\n        .take()\n        .unwrap()\n        .value\n        .try_into()\n        .unwrap();\n    Ok(poly)\n}\n\nasync fn load_remote_geojson(path: String, url: &str) -> Result<GeoJson> {\n    if !abstio::file_exists(&path) {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/importer/src/pick_geofabrik.rs#L24-L60","documentation":"load_boundary in pick_geofabrik reads a boundary file as GeoJSON and accepts only a single Feature or a FeatureCollection. Any other GeoJson variant (e.g. a bare Geometry like Polygon or GeometryCollection at the top level) hits the catch-all bail with this message, which debug-prints the parsed value.","triggerScenarios":"Calling pick_geofabrik with a boundary path whose file contains top-level GeoJson that is neither a Feature nor a FeatureCollection — e.g. a raw geometry object like {\"type\": \"Polygon\", ...} produced by hand-rolled or exported GeoJSON.","commonSituations":"Hand-editing or exporting boundary GeoJSON from tools (QGIS geometry-only export, geojson.io geometry copy) that emits a bare geometry instead of a Feature/FeatureCollection; passing a wrong file that still parses as valid GeoJSON.","solutions":["Open the boundary file and ensure the top-level object is {\"type\": \"Feature\"...} or {\"type\": \"FeatureCollection\", \"features\": [...]}; wrap a bare geometry in a Feature.","Re-export from the GIS tool as GeoJSON with features (not geometry-only).","If you control the code, add a GeoJson::Geometry arm that wraps the geometry into a Feature before matching."],"exampleFix":"// before\n{\"type\": \"Polygon\", \"coordinates\": [[...]]}\n// after\n{\"type\": \"FeatureCollection\", \"features\": [{\"type\": \"Feature\", \"properties\": {}, \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[...]]}}]}","handlingStrategy":"validation","validationCode":"let gj: GeoJson = serde_json::from_slice(&bytes)?;\nmatch gj {\n    GeoJson::Feature(_) | GeoJson::FeatureCollection(_) => {},\n    _ => return Err(anyhow!(\"boundary must be a Feature or FeatureCollection\")),\n}","typeGuard":"fn is_feature_or_collection(gj: &GeoJson) -> bool {\n    matches!(gj, GeoJson::Feature(_) | GeoJson::FeatureCollection(_))\n}","tryCatchPattern":"match load_boundary(path) {\n    Ok(poly) => poly,\n    Err(e) if e.to_string().contains(\"Unexpected geojson\") => {\n        eprintln!(\"Re-export the boundary as a Feature/FeatureCollection: {e}\");\n        fallback_poly\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always export boundaries as GeoJSON FeatureCollections, not bare geometries","Validate boundary files with geojson.io or a JSON schema check before importing","Keep a canonical preprocessed boundary file per region instead of ad-hoc exports"],"tags":["geojson","input-validation","importer"],"backgroundTag":"unexpected-response-shape","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}