a-b-street/abstreet · warning
This change had no effect.
Error message
This change had no effect.
What it means
At the end of adjust_major_minor_timing, if the transformed signal equals the original clone, the requested major/minor durations were identical to the existing timing — the call is a no-op, so the method bails to signal that nothing changed rather than reporting spurious success.
Solutions
- Compare requested durations to current stage durations and skip the call if identical
- Treat this bail as a benign no-op in batch tooling and continue
- Only call the method when an actual timing change is intended
- Collect which signals reported no-op and filter them from future runs
Example fix
// before
signal.adjust_major_minor_timing(major, minor, map)?;
// after
if signal.stages.iter().map(|s| s.stage_type.simple_duration()).collect::<Vec<_>>()
!= vec![major, minor] {
signal.adjust_major_minor_timing(major, minor, map)?;
} // otherwise it's already in the desired state Defensive patterns
Strategy: validation
Validate before calling
let current: Vec<Duration> = signal.stages.iter()
.map(|s| s.stage_type.simple_duration()).collect();
if current == vec![major, minor] {
return Ok(()); // nothing to do
} Type guard
fn timing_would_change(signal: &TrafficSignal, major: Duration, minor: Duration) -> bool {
let d: Vec<Duration> = signal.stages.iter()
.map(|s| s.stage_type.simple_duration()).collect();
d != vec![major, minor]
} Try / catch
match signal.adjust_major_minor_timing(major, minor, map) {
Err(e) if e.to_string().contains("no effect") => { /* benign no-op, continue */ }
other => other?,
} Prevention
- Skip signals already at the target timing before batch retiming
- Treat 'no effect' as idempotent success in retry loops
- Record applied timings so repeat runs don't re-apply identical values
When it happens
Trigger: Calling adjust_major_minor_timing with (major, minor) exactly equal to the current stage durations of the two stages.
Common situations: Batch retiming scripts re-applying the same values to already-tuned signals; idempotent retries where the first application already succeeded.
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
- isn't a traffic signal
- 's road already points to dst_i
- This intersection didn't already group major/minor roads…
- This intersection doesn't have 2 stages.
- This intersection doesn't have major/minor roads; they're…
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/bbc386bef335a4a9.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/objects/traffic_signals.rs:255
}
// 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 {
stage.stage_type = StageType::Fixed(minor);
}
}
if self.simple_cycle_duration() != major + minor {
bail!("This intersection didn't already group major/minor roads together.");
}
if self == &orig {
bail!("This change had no effect.");
}
Ok(())
}
pub fn missing_turns(&self, i: &Intersection) -> BTreeSet<MovementID> {
let mut missing: BTreeSet<MovementID> = i.movements.keys().cloned().collect();
for stage in &self.stages {
for m in &stage.protected_movements {
missing.remove(m);
}
for m in &stage.yield_movements {
missing.remove(m);
}
}
missing
}
View on GitHub (pinned to 0964f29315)