a-b-street/abstreet · error
No transit route from
Error message
No transit route from {} to {} now for {}! Prevent this edit What it means
While building the walking/transit input graph, transit_input_graph connects each adjacent pair of stops along a transit route with an edge whose cost is the vehicle's driving time. If the driving pathfinder (bus or train graph) finds no route between the two stops' driving positions, the invariant that a route's consecutive stops are drivable is broken, and the code panics to prevent the map edit from being saved.
Solutions
- Undo the map edit that disconnected the two stops (the message says 'Prevent this edit')
- Move one of the stops back onto a driving lane connected to the other stop before saving
- Check the route's stop list for stops placed on sidewalks or non-driving lanes and fix their driving_pos
- Verify with the map editor's connectivity/verify tools that all transit routes remain drivable before saving
Example fix
// before
match bus_graph.pathfind(req, map) {
Some(p) => input_graph.add_edge(a, b, round(p.get_cost())),
None => panic!("No transit route from {} to {}", stop1.driving_pos, stop2.driving_pos),
}
// after
match bus_graph.pathfind(req, map) {
Some(p) => input_graph.add_edge(a, b, round(p.get_cost())),
None => bail!(
"Cannot save edit: no driving path between stops {} and {} on route {}; reconnect or move the stops",
stop1.driving_pos, stop2.driving_pos, route.long_name
),
} Defensive patterns
Strategy: validation
Validate before calling
for pair in route.stops.windows(2) {
let (s1, s2) = (map.get_ts(pair[0]), map.get_ts(pair[1]));
let req = PathRequest::vehicle(s1.driving_pos, s2.driving_pos, route.route_type);
if graph.pathfind(req, map).is_none() {
return Err(anyhow!("edit would strand route {} between {} and {}", route.long_name, s1.driving_pos, s2.driving_pos));
}
} Try / catch
match result {
Ok(_) => commit_edit(),
Err(e) if e.to_string().contains("No transit route") => {
warn!("Edit rejected: {}", e);
undo_edit();
}
Err(e) => return Err(e),
} Prevention
- Run the map editor's transit connectivity check before saving edits
- Never place transit stops on lanes unreachable by the route's vehicle type
- Re-verify all routes after road or bus-lane edits
When it happens
Trigger: make_input_graph called (typically during map import or an edit in the map editor) when a transit route's consecutive stops stop1 -> stop2 cannot be connected by a driving path of the route's type, e.g. after editing roads so the stops' driving lanes become disconnected.
Common situations: Hit when a user edits the map (removes/changes roads or bus lanes) and tries to save an edit that would strand adjacent transit stops; also occurs on faulty OSM imports where bus stops sit on disconnected driving roads.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- PathConstraints::from_lt
- Negative dist_ahead?!
- expected turn, but found
- modify_step broke total_length, it's now
- Empty path
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/13d1e2e2a833b061.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/pathfind/walking.rs:386
// transit vehicle to drive between the stops. Optimistically assume no waiting time at a stop.
for route in map.all_transit_routes() {
// TODO Also plug in border starts
for pair in route.stops.windows(2) {
let (stop1, stop2) = (map.get_ts(pair[0]), map.get_ts(pair[1]));
let req = PathRequest::vehicle(stop1.driving_pos, stop2.driving_pos, route.route_type);
let maybe_driving_cost = match route.route_type {
PathConstraints::Bus => bus_graph.pathfind(req, map).map(|p| p.get_cost()),
PathConstraints::Train => train_graph.pathfind(req, map).map(|p| p.get_cost()),
_ => unreachable!(),
};
if let Some(driving_cost) = maybe_driving_cost {
input_graph.add_edge(
nodes.get(WalkingNode::RideTransit(stop1.id)),
nodes.get(WalkingNode::RideTransit(stop2.id)),
round(driving_cost),
);
} else {
panic!(
"No transit route from {} to {} now for {}! Prevent this edit",
stop1.driving_pos, stop2.driving_pos, route.long_name,
);
}
}
if let Some(l) = route.end_border {
let stop1 = map.get_ts(*route.stops.last().unwrap());
let req =
PathRequest::vehicle(stop1.driving_pos, Position::end(l, map), route.route_type);
let maybe_driving_cost = match route.route_type {
PathConstraints::Bus => bus_graph.pathfind(req, map).map(|p| p.get_cost()),
PathConstraints::Train => train_graph.pathfind(req, map).map(|p| p.get_cost()),
_ => unreachable!(),
};
if let Some(driving_cost) = maybe_driving_cost {
let border = map.get_i(map.get_l(l).dst_i);
input_graph.add_edge(View on GitHub (pinned to 0964f29315)