{"record":{"id":"f0b205f3dd86fb7e","repo":"a-b-street/abstreet","slug":"part-of-the-perimeter-goes-from-to-but-they-don-t-share-a","errorCode":null,"errorMessage":"Part of the perimeter goes from {:?} to {:?}, but they don't share a common endpoint","messagePattern":"Part of the perimeter goes from (.+?) to (.+?), but they don't share a common endpoint","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"blockfinding/src/lib.rs","lineNumber":343,"sourceCode":"                );\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            }\n        }\n        Ok(())\n    }\n\n    /// Should we reverse one perimeter to match the winding order?\n    ///\n    /// This is only meant to be called in the middle of try_to_merge. It assumes both perimeters\n    /// have already been rotated so the common roads are at the end. The invariant of first=last\n    /// is not true.\n    fn reverse_to_fix_winding_order(&self, map: &Map, other: &Perimeter) -> bool {\n        // Using geometry to determine winding order is brittle. Look for any common road, and see\n        // where it points.\n        let common_example = self.roads.last().unwrap().road;\n        let last_common_for_self = match map\n            .get_r(common_example)\n            .common_endpoint(map.get_r(wraparound_get(&self.roads, self.roads.len() as isize).road))\n        {","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/blockfinding/src/lib.rs#L325-L361","documentation":"check_continuity walks the perimeter's road list pairwise and requires each consecutive pair of roads to share a common endpoint. If two adjacent roads in the perimeter don't touch, the perimeter isn't a valid closed ring and cannot form a block, so this error bails with the two offending roads.","triggerScenarios":"Any perimeter whose roads vector has two consecutive entries (pair[i], pair[i+1]) with CommonEndpoint::None, checked after merging perimeters in try_to_merge.","commonSituations":"Map data where roads near an intersection don't geometrically connect; perimeter construction bugs that order roads incorrectly; merging perimeters split at intersections.","solutions":["Look at the two roads named in the message and verify they meet at a shared intersection in the map.","Regenerate the map/blocks from current OSM data — stale imports can contain disconnected roads.","Sort/reorder the perimeter roads so consecutive entries are actually adjacent before calling check_continuity.","Report/fix perimeter construction so roads are stored in ring order."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn is_contiguous(perimeter: &Perimeter, map: &Map) -> bool {\n    perimeter.roads.windows(2).all(|w| {\n        map.get_r(w[0].road).common_endpoint(map.get_r(w[1].road)) != CommonEndpoint::None\n    })\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only build perimeters from roads ordered as a connected ring","Check road connectivity after any map re-import","Run check_continuity as a precondition before converting to blocks"],"tags":["geometry","blocks","roads"],"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"}