{"record":{"id":"8c8f535488e23f29","repo":"a-b-street/abstreet","slug":"no-common-roads","errorCode":null,"errorMessage":"No common roads","messagePattern":"No common roads","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"blockfinding/src/lib.rs","lineNumber":209,"sourceCode":"    fn try_to_merge(\n        &mut self,\n        map: &Map,\n        other: &mut Perimeter,\n        debug_failures: bool,\n    ) -> Result<()> {\n        for reverse_to_fix_winding_order in [false, true] {\n            self.undo_invariant();\n            other.undo_invariant();\n\n            // Calculate common roads\n            let roads1: HashSet<RoadID> = self.roads.iter().map(|id| id.road).collect();\n            let roads2: HashSet<RoadID> = other.roads.iter().map(|id| id.road).collect();\n            let common: HashSet<RoadID> = roads1.intersection(&roads2).cloned().collect();\n            if common.is_empty() {\n                if debug_failures {\n                    warn!(\"No common roads\");\n                }\n                bail!(\"No common roads\");\n            }\n\n            // \"Rotate\" the order of roads, so that all of the overlapping roads are at the end of the\n            // list. If the entire perimeter is surrounded by the other, then no rotation needed.\n            if self.roads.len() != common.len() {\n                let mut i = 0;\n                while common.contains(&self.roads[0].road)\n                    || !common.contains(&self.roads.last().unwrap().road)\n                {\n                    self.roads.rotate_left(1);\n\n                    i += 1;\n                    if i == self.roads.len() {\n                        bail!(\n                            \"Rotating {:?} against common {:?} infinite-looped\",\n                            self.roads,\n                            common\n                        );","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/blockfinding/src/lib.rs#L191-L227","documentation":"Perimeter::try_to_merge merges two adjacent block perimeters, which requires them to share at least one road. The two perimeters' road sets are disjoint, so no merge is possible and the function bails (optionally warning first when debug_failures is set).","triggerScenarios":"Calling try_to_merge(other) on a Perimeter whose roads share no RoadID with `other`'s roads — the intersection of the two RoadID sets is empty.","commonSituations":"Block merging passes where blocks that merely touch at a point (or are separated by an untraced/skipped road) are fed into the merger; running merging after changing the skip set so previously adjacent blocks no longer overlap.","solutions":["Pre-check that the two perimeters share a road before calling try_to_merge and skip non-adjacent pairs.","Verify the blocks really are neighbors; only merge blocks produced from the same map and same skip configuration.","If debug_failures was enabled, read the warning output to identify which block pair is disjoint and exclude it upstream."],"exampleFix":"// before\nmerged = a.try_to_merge(map, b, &mut small_roads, debug_failures)?;\n// after\nlet a_roads: HashSet<RoadID> = a.roads.iter().map(|id| id.road).collect();\nlet b_roads: HashSet<RoadID> = b.roads.iter().map(|id| id.road).collect();\nif a_roads.is_disjoint(&b_roads) { continue; } // not adjacent, skip\nmerged = a.try_to_merge(map, b, &mut small_roads, debug_failures)?;","handlingStrategy":"validation","validationCode":"let a_set: HashSet<RoadID> = a.roads.iter().map(|rs| rs.road).collect();\nlet b_set: HashSet<RoadID> = b.roads.iter().map(|rs| rs.road).collect();\nanyhow::ensure!(!a_set.is_disjoint(&b_set), \"perimeters not adjacent\");","typeGuard":"fn perimeters_adjacent(a: &Perimeter, b: &Perimeter) -> bool {\n    let a_set: HashSet<RoadID> = a.roads.iter().map(|rs| rs.road).collect();\n    b.roads.iter().any(|rs| a_set.contains(&rs.road))\n}","tryCatchPattern":null,"preventionTips":["Only attempt merges between blocks from the same map and skip configuration.","Pre-filter block pairs by road overlap before merging."],"tags":["merge","perimeter-tracing"],"backgroundTag":"empty-result-set","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"}