{"record":{"id":"de45b325c47a3237","repo":"a-b-street/abstreet","slug":"is-in-the-past-call-sim-reset-first","errorCode":null,"errorMessage":"{} is in the past. call /sim/reset first?","messagePattern":"(.+?) is in the past\\. call /sim/reset first\\?","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"headless/src/main.rs","lineNumber":186,"sourceCode":"\n            // Also reset\n            let (new_map, new_sim) = load.setup(&mut Timer::new(\"reset sim\"));\n            *map = new_map;\n            *sim = new_sim;\n\n            Ok(\"flags changed and sim reloaded\".to_string())\n        }\n        \"/sim/load-blank\" => {\n            *map =\n                Map::load_synchronously(get(\"map\")?.to_string(), &mut Timer::new(\"load new map\"));\n            *sim = Sim::new(&map, SimOptions::default());\n            Ok(\"map changed, blank simulation\".to_string())\n        }\n        \"/sim/get-time\" => Ok(sim.time().to_string()),\n        \"/sim/goto-time\" => {\n            let t = Time::parse(get(\"t\")?)?;\n            if t <= sim.time() {\n                bail!(\"{} is in the past. call /sim/reset first?\", t)\n            } else {\n                let dt = t - sim.time();\n                sim.timed_step(map, dt, &mut None, &mut Timer::new(\"goto-time\"));\n                Ok(format!(\"it's now {}\", t))\n            }\n        }\n        \"/sim/new-person\" => {\n            let input: ExternalPerson = abstutil::from_json(body)?;\n            for trip in &input.trips {\n                if trip.departure < sim.time() {\n                    bail!(\n                        \"It's {} now, so you can't start a trip at {}\",\n                        sim.time(),\n                        trip.departure\n                    )\n                }\n            }\n","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/headless/src/main.rs#L168-L204","documentation":"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.","triggerScenarios":"POST /sim/goto-time with parameter t (parseable as Time) less than or equal to sim.time() in the headless server's handle_command.","commonSituations":"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.","solutions":["Call /sim/reset first to rewind to time 0, then /sim/goto-time to the target time.","Only issue goto-time with times strictly greater than the value from /sim/get-time.","In scripts, track or query the current time before each goto-time call and skip no-op advances."],"exampleFix":"// before\nawait post(\"/sim/goto-time\", { t: \"10:00\" });\n// after\nconst now = await post(\"/sim/get-time\");\nif (parseTime(\"10:00\") <= parseTime(now)) await post(\"/sim/reset\");\nawait post(\"/sim/goto-time\", { t: \"10:00\" });","handlingStrategy":"validation","validationCode":"const now = parseTime(await post(\"/sim/get-time\"));\nif (parseTime(target) <= now) await post(\"/sim/reset\");","typeGuard":null,"tryCatchPattern":"try {\n  await post(\"/sim/goto-time\", { t });\n} catch (e) {\n  if (String(e).includes(\"is in the past\")) {\n    await post(\"/sim/reset\");\n    await post(\"/sim/goto-time\", { t });\n  }\n}","preventionTips":["Query /sim/get-time before each goto-time","Keep target times strictly increasing within a session","Insert /sim/reset before replaying a scenario from the start"],"tags":["simulation","time","http-api"],"backgroundTag":"invalid-argument-value","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}