a-b-street/abstreet · warning
Two perimeters had every road in common
Error message
Two perimeters had every road in common: {:?} What it means
In blockfinding, when merging two perimeters into one block, the merge algorithm found that both perimeters consisted of exactly the same set of roads, leaving an empty road list for the merged perimeter. This signals an internal degenerate case introduced by find_roads_to_skip_tracing; the library logs a warning (in debug mode) and aborts the merge rather than produce an invalid empty block.
Solutions
- Re-run blockfinding on the latest map import; this is a known internal edge case that map/preprocessing fixes may eliminate.
- Check the map for duplicate or degenerate road rings around the block in question.
- If you maintain this code, extend the merge logic (find_roads_to_skip_tracing) so the common-roads case resolves to a single perimeter instead of an empty one.
- Filter or log the failing block candidate and continue with other blocks rather than treating it as fatal.
Example fix
// before
bail!("Two perimeters had every road in common: {:?}", common);
// after
if common.len() == 1 {
// degenerate two-road ring: skip merge instead of failing
return Ok(None);
}
bail!("Two perimeters had every road in common: {:?}", common); Defensive patterns
Strategy: try-catch
Try / catch
match perimeter1.try_to_merge(perimeter2, map, ...) {
Err(e) if e.to_string().contains("every road in common") => {
warn!("skipping degenerate block: {}", e);
}
other => other?,
} Prevention
- Regenerate blocks on freshly imported maps
- Deduplicate candidate perimeters before merging
- Treat this failure as skip-this-block, not fatal
When it happens
Trigger: Calling Perimeter::try_to_merge (via block generation) when two candidate perimeters have identical road sets, so self.roads becomes empty after moving all common roads into the interior.
Common situations: Map data with duplicate ring roads or overlapping perimeters; running the blockfinder over areas with cul-de-sacs or dual-carriageway loops that produce two perimeters covering the same roads.
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
- A merged perimeter couldn't be blockified
- Part of the perimeter goes from
- and don't share a common endpoint
- ( ) is a border, but is connected to >1 road
- isn't an endpoint of
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/61d8545ad115acf8.
Report an issue: GitHub.
Appendix: source
Thrown at blockfinding/src/lib.rs:309
);
}
}
// Very straightforward snipping now
for _ in 0..common.len() {
self.roads.pop().unwrap();
other.roads.pop().unwrap();
}
// This order assumes everything is clockwise to start with.
self.roads.append(&mut other.roads);
// TODO This case was introduced with find_roads_to_skip_tracing. Not sure why.
if self.roads.is_empty() {
if debug_failures {
warn!("Two perimeters had every road in common: {:?}", common);
}
bail!("Two perimeters had every road in common: {:?}", common);
}
self.interior.extend(common);
self.interior.append(&mut other.interior);
// Restore the first=last invariant
self.restore_invariant();
// Make sure we didn't wind up with any internal dead-ends
self.collapse_deadends();
if let Err(err) = self.check_continuity(map) {
debug!(
"A merged perimeter couldn't be blockified: {}. {:?}",
err, self
);
bail!(
"A merged perimeter couldn't be blockified: {}. {:?}",View on GitHub (pinned to 0964f29315)