a-b-street/abstreet · warning
A merged perimeter couldn't be blockified
Error message
A merged perimeter couldn't be blockified: {}. {:?} What it means
After merging two perimeters, the library runs check_continuity to verify the resulting road sequence still forms a connected loop. If the merged perimeter is broken (adjacent roads don't share endpoints), the merge is rejected with this error, including the continuity error and the perimeter in the message.
Solutions
- Inspect the printed perimeter to find the two adjacent roads lacking a shared endpoint.
- Reverse one of the input perimeters before merging if winding order is mismatched (see Should we reverse one perimeter to match the winding order).
- Re-generate blocks from a freshly imported map; corrupted road orderings often stem from stale cached blockfinding data.
- If maintaining the code, fix the merge to interleave roads (not just concatenate) when perimeters were split mid-block.
Defensive patterns
Strategy: try-catch
Validate before calling
let ordered = perimeter.roads.windows(2).all(|w| map.get_r(w[0].road).common_endpoint(map.get_r(w[1].road)) != CommonEndpoint::None);
Try / catch
if let Err(e) = perimeter.check_continuity(map) {
log::debug!("skipping non-contiguous perimeter: {}", e);
return Ok(None);
} Prevention
- Verify perimeter continuity right after construction
- Handle winding-order reversal before merging
- Regenerate cached blocks after map edits
When it happens
Trigger: Perimeter::try_to_merge succeeds in joining two perimeters' road lists, but check_continuity fails because the concatenation order leaves a gap between adjacent roads.
Common situations: Block generation on complex intersections where perimeters interleave; merging perimeters built from non-adjacent road sets; stale map data producing non-contiguous road orderings.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Two perimeters had every road in common
- 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/b1e70a29d457ff19.
Report an issue: GitHub.
Appendix: source
Thrown at blockfinding/src/lib.rs:326
}
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: {}. {:?}",
err,
self
);
}
return Ok(());
}
unreachable!()
}
fn check_continuity(&self, map: &Map) -> Result<()> {
for pair in self.roads.windows(2) {
let r1 = map.get_r(pair[0].road);
let r2 = map.get_r(pair[1].road);
if r1.common_endpoint(r2) == CommonEndpoint::None {
bail!("Part of the perimeter goes from {:?} to {:?}, but they don't share a common endpoint", pair[0], pair[1]);
}View on GitHub (pinned to 0964f29315)