{"record":{"id":"b1e70a29d457ff19","repo":"a-b-street/abstreet","slug":"a-merged-perimeter-couldn-t-be-blockified","errorCode":null,"errorMessage":"A merged perimeter couldn't be blockified: {}. {:?}","messagePattern":"A merged perimeter couldn't be blockified: (.+?)\\. (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"blockfinding/src/lib.rs","lineNumber":326,"sourceCode":"                }\n                bail!(\"Two perimeters had every road in common: {:?}\", common);\n            }\n\n            self.interior.extend(common);\n            self.interior.append(&mut other.interior);\n\n            // Restore the first=last invariant\n            self.restore_invariant();\n\n            // Make sure we didn't wind up with any internal dead-ends\n            self.collapse_deadends();\n\n            if let Err(err) = self.check_continuity(map) {\n                debug!(\n                    \"A merged perimeter couldn't be blockified: {}. {:?}\",\n                    err, self\n                );\n                bail!(\n                    \"A merged perimeter couldn't be blockified: {}. {:?}\",\n                    err,\n                    self\n                );\n            }\n\n            return Ok(());\n        }\n        unreachable!()\n    }\n\n    fn check_continuity(&self, map: &Map) -> Result<()> {\n        for pair in self.roads.windows(2) {\n            let r1 = map.get_r(pair[0].road);\n            let r2 = map.get_r(pair[1].road);\n            if r1.common_endpoint(r2) == CommonEndpoint::None {\n                bail!(\"Part of the perimeter goes from {:?} to {:?}, but they don't share a common endpoint\", pair[0], pair[1]);\n            }","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/blockfinding/src/lib.rs#L308-L344","documentation":"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.","triggerScenarios":"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.","commonSituations":"Block generation on complex intersections where perimeters interleave; merging perimeters built from non-adjacent road sets; stale map data producing non-contiguous road orderings.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"let ordered = perimeter.roads.windows(2).all(|w| map.get_r(w[0].road).common_endpoint(map.get_r(w[1].road)) != CommonEndpoint::None);","typeGuard":null,"tryCatchPattern":"if let Err(e) = perimeter.check_continuity(map) {\n    log::debug!(\"skipping non-contiguous perimeter: {}\", e);\n    return Ok(None);\n}","preventionTips":["Verify perimeter continuity right after construction","Handle winding-order reversal before merging","Regenerate cached blocks after map edits"],"tags":["geometry","blocks","continuity"],"backgroundTag":"invalid-state-transition","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}