a-b-street/abstreet · error
Infinite loop starting from
Error message
Infinite loop starting from {start} ({}) What it means
The perimeter walk exceeded one step per road in the whole map without returning to the start road side, so the trace must be looping forever. The library bails and reports the start lane and its parent road's original OSM ID to help locate the pathological geometry.
Solutions
- Check the reported start road's geometry (orig_id) for loops or duplicate ways in the OSM source and fix them.
- Adjust the `skip` set so the trace can't oscillate between the problematic roads.
- Retry after regenerating the map with a newer map_model import, which may fix road-side sorting.
Defensive patterns
Strategy: try-catch
Try / catch
if let Err(e) = Perimeter::single_block(map, start, &skip) {
if e.to_string().contains("Infinite loop") {
// record start road orig_id for map QA; skip this seed
continue;
}
return Err(e);
} Prevention
- Log the reported orig_id and audit that way's OSM geometry for loops.
- Keep skip sets for oscillation-prone one-way pairs.
When it happens
Trigger: single_block's loop visits more roads than map.all_roads().len() without current_road_side returning to start_road_side — a cycle in the road-side graph that never closes on the start side.
Common situations: Bug-prone map geometry such as figure-eight loop roads, one-way pairs the walker oscillates between, or maps corrupted by edits; also seen with unusual skip sets that break the expected clockwise ordering.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Started on a road we shouldn't trace
- Starting on inner piece of a loop road
- hit the map boundary at
- Looped back on the same road, but not at a dead-end
- No common roads
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/c8b1424d87ea0cb8.
Report an issue: GitHub.
Appendix: source
Thrown at blockfinding/src/lib.rs:106
if next.road == current_road_side.road {
if sorted_roads.len() != 2 {
bail!("Looped back on the same road, but not at a dead-end");
}
}
}
roads.push(current_road_side);
current_road_side = next;
current_intersection = map
.get_r(current_road_side.road)
.other_endpt(current_intersection);
if current_road_side == start_road_side {
roads.push(start_road_side);
break;
}
if roads.len() > map.all_roads().len() {
bail!(
"Infinite loop starting from {start} ({})",
map.get_parent(start).orig_id
);
}
}
assert_eq!(roads[0], *roads.last().unwrap());
Ok(Perimeter {
roads,
interior: BTreeSet::new(),
})
}
/// This calculates all single block perimeters for the entire map. The resulting list does not
/// cover roads near the map boundary.
pub fn find_all_single_blocks(map: &Map) -> Vec<Perimeter> {
let skip = Perimeter::find_roads_to_skip_tracing(map);
let mut seen = HashSet::new();View on GitHub (pinned to 0964f29315)