a-b-street/abstreet · error

Traffic signal does not allow enough time in stage to…

Error message

Traffic signal does not allow enough time in stage to complete the crosswalk
Stage Index{}
Stage : {:?}
Time Required: {}
Time Given: {}

What it means

TrafficSignal::validate ensures each stage's duration is at least the minimum time a pedestrian needs to finish crossing any crosswalk in that stage. If the stage's simple_duration is shorter than get_min_crossing_time for the stage, the signal would strand pedestrians mid-crossing, so import bails.

Solutions

  1. Increase the stage's Fixed duration to at least get_min_crossing_time for that stage index
  2. Shorten or reroute the crosswalk, or move the crosswalk's protected time to a longer stage
  3. Use adjust_major_minor_timing or another helper that respects minimum crossing times instead of raw duration edits
  4. Compute and print min crossing times per stage before tuning durations

Example fix

// before
stage.stage_type = StageType::Fixed(Duration::seconds(5));
// after
let min_time = signal.get_min_crossing_time(stage_idx, intersection);
stage.stage_type = StageType::Fixed(min_time.max(Duration::seconds(5)));
Defensive patterns

Strategy: validation

Validate before calling

let need = signal.get_min_crossing_time(idx, i);
if stage.stage_type.simple_duration() < need {
    stage.stage_type = StageType::Fixed(need);
}

Type guard

fn duration_safe(stage: &Stage, idx: usize, i: &Intersection) -> bool {
    stage.stage_type.simple_duration() >= /* need access via signal */ true
}

Try / catch

match signal.adjust_stage_duration(idx, new_time, i) {
    Err(e) => eprintln!("raise duration or shorten crosswalk: {e}"),
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Setting a StageType::Fixed duration below the computed pedestrian crossing time for the stage's crosswalks (long/wide crossings, or very short stage times); affecting pedestrian and unprotected turn movements in the stage.

Common situations: Aggressively shortening cycle times to reduce car delay; importing maps where crosswalks span very wide roads; tuning signal timings by hand without checking walk times.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/a0e9dde50f4aa8e2. Report an issue: GitHub.

Appendix: source

Thrown at map_model/src/objects/traffic_signals.rs:130

                        bail!(
                            "Traffic signal has conflicting protected movements in one \
                             stage:\n{:?}\n\n{:?}",
                            m1,
                            m2
                        );
                    }
                }
            }

            // Do any of the crosswalks yield?
            for m in stage.yield_movements.iter().map(|m| &i.movements[m]) {
                // TODO Maybe make UnmarkedCrossing yield
                assert!(!m.turn_type.pedestrian_crossing())
            }
            // Is there enough time in each stage to walk across the crosswalk
            let min_crossing_time = self.get_min_crossing_time(stage_index, i);
            if stage.stage_type.simple_duration() < min_crossing_time {
                bail!(
                    "Traffic signal does not allow enough time in stage to complete the \
                     crosswalk\nStage Index{}\nStage : {:?}\nTime Required: {}\nTime Given: {}",
                    stage_index,
                    stage,
                    min_crossing_time,
                    stage.stage_type.simple_duration()
                );
            }
        }
        Ok(())
    }

    /// Move crosswalks from stages, adding them to an all-walk as last stage. This may promote
    /// yields to protected. True is returned if any stages were added or modified.
    pub fn convert_to_ped_scramble(&mut self, i: &Intersection) -> bool {
        self.internal_convert_to_ped_scramble(true, i)
    }
    /// Move crosswalks from stages, adding them to an all-walk as last stage. This does not promote

View on GitHub (pinned to 0964f29315)