a-b-street/abstreet · error
This intersection doesn't have major/minor roads; they're…
Error message
This intersection doesn't have major/minor roads; they're all the same rank.
What it means
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.
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
Example fix
// before
signal.adjust_major_minor_timing(major, minor, map)?;
// after
let ranks: BTreeSet<usize> = map.get_i(signal.id).roads.iter()
.map(|r| map.get_r(*r).get_detailed_rank()).collect();
if ranks.len() > 1 {
signal.adjust_major_minor_timing(major, minor, map)?;
} Defensive patterns
Strategy: validation
Validate before calling
let ranks: BTreeSet<usize> = map.get_i(signal.id).roads.iter()
.map(|r| map.get_r(*r).get_detailed_rank()).collect();
if ranks.len() < 2 {
return Err(anyhow!("no major/minor split at {}", signal.id));
} Type guard
fn has_rank_split(map: &Map, signal: &TrafficSignal) -> bool {
let ranks: BTreeSet<usize> = map.get_i(signal.id).roads.iter()
.map(|r| map.get_r(*r).get_detailed_rank()).collect();
ranks.len() > 1
} Try / catch
if has_rank_split(map, signal) {
signal.adjust_major_minor_timing(major, minor, map)?;
} else {
apply_uniform_timing(signal, major + minor);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- This intersection doesn't have 2 stages.
- This intersection doesn't use fixed timing.
- This intersection didn't already group major/minor roads…
- isn't a traffic signal
- Traffic signal assignment for
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/b5e1a51790aa35ce.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/objects/traffic_signals.rs:227
&mut self,
major: Duration,
minor: Duration,
map: &Map,
) -> Result<()> {
if self.stages.len() != 2 {
bail!("This intersection doesn't have 2 stages.");
}
// What's the rank of each road?
let mut rank_per_road: BTreeMap<RoadID, usize> = BTreeMap::new();
for r in &map.get_i(self.id).roads {
rank_per_road.insert(*r, map.get_r(*r).get_detailed_rank());
}
let mut ranks: Vec<usize> = rank_per_road.values().cloned().collect();
ranks.sort_unstable();
ranks.dedup();
if ranks.len() == 1 {
bail!("This intersection doesn't have major/minor roads; they're all the same rank.");
}
let highest_rank = ranks.pop().unwrap();
// Try to apply the transformation
let orig = self.clone();
for stage in &mut self.stages {
match stage.stage_type {
StageType::Fixed(_) => {}
_ => bail!("This intersection doesn't use fixed timing."),
}
// Ignoring crosswalks, do any of the turns come from a major road?
if stage
.protected_movements
.iter()
.any(|m| !m.crosswalk && highest_rank == rank_per_road[&m.from.road])
{
stage.stage_type = StageType::Fixed(major);
} else {View on GitHub (pinned to 0964f29315)