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

  1. Compare requested durations to current stage durations and skip the call if identical
  2. Treat this bail as a benign no-op in batch tooling and continue
  3. Only call the method when an actual timing change is intended
  4. 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

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


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)