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

  1. Check the reported start road's geometry (orig_id) for loops or duplicate ways in the OSM source and fix them.
  2. Adjust the `skip` set so the trace can't oscillate between the problematic roads.
  3. 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

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


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)