a-b-street/abstreet · error

Rotating against common infinite-looped

Error message

Rotating {:?} against common {:?} infinite-looped

What it means

While merging, try_to_merge rotates `self`'s road-side list left so that the roads common with `other` sit at the end of the list. If after rotating a full list length the last road is still not common (or the first still is), the rotation can never succeed — the common roads aren't contiguous in the list — so it bails instead of looping forever.

Solutions

  1. Skip merging this block pair and retry merging later/with other pairs (find_blocks typically retries merges).
  2. Inspect the two perimeters' road sequences to understand why common roads are scattered; fix upstream block formation geometry.
  3. Reduce reliance on merge for these shapes: keep them as separate blocks instead of forcing a merge.
Defensive patterns

Strategy: fallback

Try / catch

match a.try_to_merge(map, b, &mut small_roads, debug_failures) {
    Ok(_) => {},
    Err(e) if e.to_string().contains("Rotating") => { /* defer pair to a later merge pass */ }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: try_to_merge called where `self.roads` contains common roads in a non-contiguous arrangement and self.roads.len() != common.len(), so the rotate_left loop runs self.roads.len() times without satisfying the ordering condition.

Common situations: Two blocks interleave along each other (share roads in several separated runs) due to unusual geometry like finger-shaped blocks or blocks sharing multiple disjoint stretches; map data with oddly shaped blocks from complex intersections.

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/6775801fdcef1a47. Report an issue: GitHub.

Appendix: source

Thrown at blockfinding/src/lib.rs:223

            if common.is_empty() {
                if debug_failures {
                    warn!("No common roads");
                }
                bail!("No common roads");
            }

            // "Rotate" the order of roads, so that all of the overlapping roads are at the end of the
            // list. If the entire perimeter is surrounded by the other, then no rotation needed.
            if self.roads.len() != common.len() {
                let mut i = 0;
                while common.contains(&self.roads[0].road)
                    || !common.contains(&self.roads.last().unwrap().road)
                {
                    self.roads.rotate_left(1);

                    i += 1;
                    if i == self.roads.len() {
                        bail!(
                            "Rotating {:?} against common {:?} infinite-looped",
                            self.roads,
                            common
                        );
                    }
                }
            }
            // Same thing with the other
            if other.roads.len() != common.len() {
                let mut i = 0;
                while common.contains(&other.roads[0].road)
                    || !common.contains(&other.roads.last().unwrap().road)
                {
                    other.roads.rotate_left(1);

                    i += 1;
                    if i == other.roads.len() {
                        bail!(

View on GitHub (pinned to 0964f29315)