a-b-street/abstreet · error

Bad TimeInterval ..

Error message

Bad TimeInterval {} .. {}

What it means

TimeInterval::new validates that the interval start precedes its end on the simulation time axis. If end < start the interval is meaningless (negative duration) so the library panics immediately rather than constructing a broken interval that would corrupt later percent()/duration math.

Solutions

  1. Swap the arguments so TimeInterval::new receives the earlier time first
  2. Sort or validate the source times before constructing the interval
  3. Debug-print the two Time values; if they come from user data, fix the producer

Example fix

// before
let iv = TimeInterval::new(end, start);
// after
let iv = TimeInterval::new(start.min(end), start.max(end));
Defensive patterns

Strategy: validation

Validate before calling

fn safe_time_interval(start: Time, end: Time) -> Option<TimeInterval> {
    if end < start { None } else { Some(TimeInterval::new(start, end)) }
}

Type guard

fn valid_time_interval(start: Time, end: Time) -> bool { end >= start }

Try / catch

let iv = std::panic::catch_unwind(|| TimeInterval::new(start, end)).ok();

Prevention

When it happens

Trigger: Calling TimeInterval::new(start, end) with an end Time earlier than the start Time, e.g. by passing arguments in the wrong order or computing times from reversed data.

Common situations: Scenario files or CSV inputs where timestamps are swapped; deriving start/end from sorted-incorrect data; off-by-one when converting raw seconds; callers hand-building intervals for map UI slices.

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/66281dda7fdf1620. Report an issue: GitHub.

Appendix: source

Thrown at sim/src/lib.rs:505

        assert!(pos.dist_along() <= lane.length());
        SidewalkSpot {
            sidewalk_pos: pos,
            connection: SidewalkPOI::SuddenlyAppear,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
pub(crate) struct TimeInterval {
    // TODO Private fields
    pub start: Time,
    pub end: Time,
}

impl TimeInterval {
    pub fn new(start: Time, end: Time) -> TimeInterval {
        if end < start {
            panic!("Bad TimeInterval {} .. {}", start, end);
        }
        TimeInterval { start, end }
    }

    pub fn percent(&self, t: Time) -> f64 {
        if self.start == self.end {
            return 1.0;
        }

        let x = (t - self.start) / (self.end - self.start);
        assert!((0.0..=1.0).contains(&x));
        x
    }

    pub fn percent_clamp_end(&self, t: Time) -> f64 {
        if t > self.end {
            return 1.0;
        }

View on GitHub (pinned to 0964f29315)