{"record":{"id":"b5e1a51790aa35ce","repo":"a-b-street/abstreet","slug":"this-intersection-doesn-t-have-major-minor-roads-they-re-all","errorCode":null,"errorMessage":"This intersection doesn't have major/minor roads; they're all the same rank.","messagePattern":"This intersection doesn't have major/minor roads; they're all the same rank\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/objects/traffic_signals.rs","lineNumber":227,"sourceCode":"        &mut self,\n        major: Duration,\n        minor: Duration,\n        map: &Map,\n    ) -> Result<()> {\n        if self.stages.len() != 2 {\n            bail!(\"This intersection doesn't have 2 stages.\");\n        }\n\n        // What's the rank of each road?\n        let mut rank_per_road: BTreeMap<RoadID, usize> = BTreeMap::new();\n        for r in &map.get_i(self.id).roads {\n            rank_per_road.insert(*r, map.get_r(*r).get_detailed_rank());\n        }\n        let mut ranks: Vec<usize> = rank_per_road.values().cloned().collect();\n        ranks.sort_unstable();\n        ranks.dedup();\n        if ranks.len() == 1 {\n            bail!(\"This intersection doesn't have major/minor roads; they're all the same rank.\");\n        }\n        let highest_rank = ranks.pop().unwrap();\n\n        // Try to apply the transformation\n        let orig = self.clone();\n        for stage in &mut self.stages {\n            match stage.stage_type {\n                StageType::Fixed(_) => {}\n                _ => bail!(\"This intersection doesn't use fixed timing.\"),\n            }\n            // Ignoring crosswalks, do any of the turns come from a major road?\n            if stage\n                .protected_movements\n                .iter()\n                .any(|m| !m.crosswalk && highest_rank == rank_per_road[&m.from.road])\n            {\n                stage.stage_type = StageType::Fixed(major);\n            } else {","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/objects/traffic_signals.rs#L209-L245","documentation":"Within adjust_major_minor_timing, after ranking each road by get_detailed_rank, if all roads at the intersection share a single distinct rank there is no 'major' versus 'minor' split to apply, so the method bails.","triggerScenarios":"Calling adjust_major_minor_timing on an intersection whose roads all have identical rank (all residential, or all same class) — the sorted, deduped ranks vector has length 1.","commonSituations":"Applying major/minor retiming to an all-minor intersection; map data where road classification changed so the former major road was downgraded to match its neighbors.","solutions":["Only call this method on intersections with a genuine rank difference; check ranks beforehand","Reclassify the major road in OSM or map edits so it outranks the others","Use a uniform-timing transformation (directly setting stage durations) for equal-rank intersections","Skip these intersections and log them for manual review"],"exampleFix":"// before\nsignal.adjust_major_minor_timing(major, minor, map)?;\n// after\nlet ranks: BTreeSet<usize> = map.get_i(signal.id).roads.iter()\n    .map(|r| map.get_r(*r).get_detailed_rank()).collect();\nif ranks.len() > 1 {\n    signal.adjust_major_minor_timing(major, minor, map)?;\n}","handlingStrategy":"validation","validationCode":"let ranks: BTreeSet<usize> = map.get_i(signal.id).roads.iter()\n    .map(|r| map.get_r(*r).get_detailed_rank()).collect();\nif ranks.len() < 2 {\n    return Err(anyhow!(\"no major/minor split at {}\", signal.id));\n}","typeGuard":"fn has_rank_split(map: &Map, signal: &TrafficSignal) -> bool {\n    let ranks: BTreeSet<usize> = map.get_i(signal.id).roads.iter()\n        .map(|r| map.get_r(*r).get_detailed_rank()).collect();\n    ranks.len() > 1\n}","tryCatchPattern":"if has_rank_split(map, signal) {\n    signal.adjust_major_minor_timing(major, minor, map)?;\n} else {\n    apply_uniform_timing(signal, major + minor);\n}","preventionTips":["Verify road ranks differ before major/minor retiming","Handle equal-rank intersections with uniform timing instead","Fix road classification data if a major road lost its rank"],"tags":["traffic-signals","road-rank","precondition"],"backgroundTag":"invalid-argument-value","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"}