a-b-street/abstreet · error
No valid stops
Error message
No valid stops
What it means
While creating a transit route, each GTFS stop id must have been successfully created earlier (gtfs_to_stop_id). If none of the route's stops resolved to a TransitStopID, the route has no valid stops and cannot be constructed, so create_route bails.
Solutions
- Fix the upstream stop-creation failures (see stop-level errors) so at least one stop resolves.
- Check that route stop gtfs_ids match the ids in the stops feed (no key mismatch).
- Skip the route (the importer does) or trim the GTFS feed to routes with importable stops.
Defensive patterns
Strategy: validation
Validate before calling
let valid: Vec<_> = route.stops.iter()
.filter(|id| gtfs_to_stop_id.contains_key(*id)).count();
if valid == 0 { /* skip route entirely before create_route */ } Try / catch
match create_route(...) {
Err(e) if e.to_string() == "No valid stops" => {
warn!("skipping route {:?}", route.gtfs_id); Ok(None)
}
other => other.map(Some),
} Prevention
- Fix stop-level import failures before importing routes
- Cross-check gtfs_id keys between stops.txt and routes data
- Import routes only after verifying at least one stop resolves
When it happens
Trigger: finalize_transit -> create_route where every stop id in route.stops failed create_stop (or was filtered out), leaving the filtered stops vec empty.
Common situations: Routes whose stops all lie outside the map boundary or near sidewalks that couldn't snap; partial GTFS imports where stop creation failed wholesale for a route; mismatched gtfs_id keys between stops and routes feeds.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- Couldn't find a lane for
- Stop position wasn't close to a sidewalk
- Couldn't find a border near start
- Couldn't find a border near end
- couldn't find where shape enters map
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/4d25868be5f6fbb4.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/make/transit.rs:157
}
snapper
}
}
fn create_route(
route: &RawTransitRoute,
map: &mut Map,
gtfs_to_stop_id: &HashMap<String, TransitStopID>,
snapper: &BorderSnapper,
) -> Result<()> {
// TODO At least warn about stops that failed to snap
let stops: Vec<TransitStopID> = route
.stops
.iter()
.filter_map(|gtfs_id| gtfs_to_stop_id.get(gtfs_id).cloned())
.collect();
if stops.is_empty() {
bail!("No valid stops");
}
let border_snap_threshold = Distance::meters(30.0);
let start = if map.boundary_polygon.contains_pt(route.shape.first_pt()) {
map.get_ts(stops[0]).driving_pos.lane()
} else {
// Find the first time the route shape hits the map boundary
let entry_pt = *map
.boundary_polygon
.get_outer_ring()
.all_intersections(&route.shape)
.get(0)
.ok_or_else(|| anyhow!("couldn't find where shape enters map"))?;
// Snap that to a border
let borders = if route.route_type == RawTransitType::Bus {
&snapper.bus_incoming_borders
} else {
&snapper.train_incoming_bordersView on GitHub (pinned to 0964f29315)