a-b-street/abstreet · error

Starting on inner piece of a loop road

Error message

Starting on inner piece of a loop road

What it means

The start lane is on a loop road (a road whose src and dst intersection are the same), but on the inner direction that isn't represented among the intersection's sorted road sides. Tracing from such a lane cannot produce a valid perimeter, so the library refuses the start point.

Solutions

  1. Pick a different lane on the loop road whose side appears in the intersection's sorted road sides.
  2. Add the loop road to the `skip` set and start tracing elsewhere.
  3. If you must trace loops, handle them specially outside single_block (the library explicitly doesn't support inner loop starts).

Example fix

// before
let perimeter = Perimeter::single_block(map, some_lane_on_loop, &skip)?;
// after
let r = map.get_parent(some_lane_on_loop);
if r.src_i == r.dst_i {
    some_lane_on_loop = pick_outer_loop_lane(map, r.id); // side present in sorted road sides
}
let perimeter = Perimeter::single_block(map, some_lane_on_loop, &skip)?;
Defensive patterns

Strategy: validation

Validate before calling

let r = map.get_parent(start);
if r.src_i == r.dst_i {
    let i = map.get_i(r.src_i);
    let side = map.get_l(start).get_nearest_side_of_road(map);
    anyhow::ensure!(i.get_road_sides_sorted(map).contains(&side), "inner loop start");
}

Type guard

fn is_inner_loop_start(map: &Map, start: LaneID) -> bool {
    let r = map.get_parent(start);
    if r.src_i != r.dst_i { return false; }
    let side = map.get_l(start).get_nearest_side_of_road(map);
    !map.get_i(r.src_i).get_road_sides_sorted(map).contains(&side)
}

Prevention

When it happens

Trigger: Calling Perimeter::single_block with a start lane whose parent road is a loop (get_parent(start).src_i == dst_i) and where i.get_road_sides_sorted(map) does not contain the lane's nearest road side.

Common situations: Roundabouts and loop ramps in OSM data frequently produce one-way loop roads; picking an arbitrary lane on such a road as the trace start hits the inner direction.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/8649626a1957e46c. Report an issue: GitHub.

Appendix: source

Thrown at blockfinding/src/lib.rs:61

    /// Starting at any lane, snap to the nearest side of that road, then begin tracing a single
    /// block, with no interior roads. This will fail if a map boundary is reached. The results are
    /// unusual when crossing the entrance to a tunnel or bridge, and so `skip` is used to avoid
    /// tracing there.
    pub fn single_block(map: &Map, start: LaneID, skip: &HashSet<RoadID>) -> Result<Perimeter> {
        let mut roads = Vec::new();
        let start_road_side = map.get_l(start).get_nearest_side_of_road(map);

        if skip.contains(&start_road_side.road) {
            bail!("Started on a road we shouldn't trace");
        }

        // We may start on a loop road on the "inner" direction
        {
            let start_r = map.get_parent(start);
            if start_r.src_i == start_r.dst_i {
                let i = map.get_i(start_r.src_i);
                if !i.get_road_sides_sorted(map).contains(&start_road_side) {
                    bail!("Starting on inner piece of a loop road");
                }
            }
        }

        // We need to track which side of the road we're at, but also which direction we're facing
        let mut current_road_side = start_road_side;
        let mut current_intersection = map.get_l(start).dst_i;
        loop {
            let i = map.get_i(current_intersection);
            if i.is_border() {
                bail!("hit the map boundary at {}", i.orig_id);
            }
            let mut sorted_roads = i.get_road_sides_sorted(map);
            sorted_roads.retain(|id| !skip.contains(&id.road));

            let idx = sorted_roads
                .iter()
                .position(|x| *x == current_road_side)

View on GitHub (pinned to 0964f29315)