a-b-street/abstreet · error

is in the past. call /sim/reset first?

Error message

{} is in the past. call /sim/reset first?

What it means

The headless simulation server's /sim/goto-time command only advances time forward. If the requested target time is less than or equal to the current simulation time, there is no rewind support, so the server rejects the request and suggests calling /sim/reset first.

Solutions

  1. Call /sim/reset first to rewind to time 0, then /sim/goto-time to the target time.
  2. Only issue goto-time with times strictly greater than the value from /sim/get-time.
  3. In scripts, track or query the current time before each goto-time call and skip no-op advances.

Example fix

// before
await post("/sim/goto-time", { t: "10:00" });
// after
const now = await post("/sim/get-time");
if (parseTime("10:00") <= parseTime(now)) await post("/sim/reset");
await post("/sim/goto-time", { t: "10:00" });
Defensive patterns

Strategy: validation

Validate before calling

const now = parseTime(await post("/sim/get-time"));
if (parseTime(target) <= now) await post("/sim/reset");

Try / catch

try {
  await post("/sim/goto-time", { t });
} catch (e) {
  if (String(e).includes("is in the past")) {
    await post("/sim/reset");
    await post("/sim/goto-time", { t });
  }
}

Prevention

When it happens

Trigger: POST /sim/goto-time with parameter t (parseable as Time) less than or equal to sim.time() in the headless server's handle_command.

Common situations: Replaying a scenario from an earlier timestep; a script computes times in a loop without tracking monotonic increase; rerunning the same goto-time call twice.

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

Appendix: source

Thrown at headless/src/main.rs:186

            // Also reset
            let (new_map, new_sim) = load.setup(&mut Timer::new("reset sim"));
            *map = new_map;
            *sim = new_sim;

            Ok("flags changed and sim reloaded".to_string())
        }
        "/sim/load-blank" => {
            *map =
                Map::load_synchronously(get("map")?.to_string(), &mut Timer::new("load new map"));
            *sim = Sim::new(&map, SimOptions::default());
            Ok("map changed, blank simulation".to_string())
        }
        "/sim/get-time" => Ok(sim.time().to_string()),
        "/sim/goto-time" => {
            let t = Time::parse(get("t")?)?;
            if t <= sim.time() {
                bail!("{} is in the past. call /sim/reset first?", t)
            } else {
                let dt = t - sim.time();
                sim.timed_step(map, dt, &mut None, &mut Timer::new("goto-time"));
                Ok(format!("it's now {}", t))
            }
        }
        "/sim/new-person" => {
            let input: ExternalPerson = abstutil::from_json(body)?;
            for trip in &input.trips {
                if trip.departure < sim.time() {
                    bail!(
                        "It's {} now, so you can't start a trip at {}",
                        sim.time(),
                        trip.departure
                    )
                }
            }

View on GitHub (pinned to 0964f29315)