{"record":{"id":"ed98f791169bcea3","repo":"a-b-street/abstreet","slug":"person-starts-two-trips-in-the-wrong-order-then","errorCode":null,"errorMessage":"Person ({:?}) starts two trips in the wrong order: {} then {}","messagePattern":"Person \\((.+?)\\) starts two trips in the wrong order: (.+?) then (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"synthpop/src/scenario.rs","lineNumber":173,"sourceCode":"                    return x.to_string();\n                }\n            }\n        }\n        // Dynamically generated -- arguably this is an absence of a default scenario\n        \"home_to_work\".to_string()\n    }\n}\n\nimpl PersonSpec {\n    /// Verify that a person's trips make sense\n    pub fn check_schedule(&self) -> Result<()> {\n        if self.trips.is_empty() {\n            bail!(\"Person ({:?}) has no trips at all\", self.orig_id);\n        }\n\n        for pair in self.trips.windows(2) {\n            if pair[0].depart >= pair[1].depart {\n                bail!(\n                    \"Person ({:?}) starts two trips in the wrong order: {} then {}\",\n                    self.orig_id,\n                    pair[0].depart,\n                    pair[1].depart\n                );\n            }\n\n            if pair[0].destination != pair[1].origin {\n                // Exiting one border and re-entering another is fine\n                if matches!(pair[0].destination, TripEndpoint::Border(_))\n                    && matches!(pair[1].origin, TripEndpoint::Border(_))\n                {\n                    continue;\n                }\n                bail!(\n                    \"Person ({:?}) warps from {:?} to {:?} during adjacent trips\",\n                    self.orig_id,\n                    pair[0].destination,","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/synthpop/src/scenario.rs#L155-L191","documentation":"check_schedule requires trips to be sorted by departure time. If any adjacent pair has pair[0].depart >= pair[1].depart, the schedule is invalid and this error names the person and both departure times.","triggerScenarios":"Building a PersonSpec whose trips are not strictly increasing in departure time, including trips that depart at exactly the same time.","commonSituations":"External population files with unsorted trips, merging multiple data sources without re-sorting, floating/second-granularity collisions producing equal timestamps.","solutions":["Sort the trips by departure time before calling check_schedule","Break ties by nudging one departure slightly later or merging trips","Validate/sort in the import pipeline before constructing the scenario"],"exampleFix":"// before\nlet spec = PersonSpec { trips: trips, .. };\n// after\ntrips.sort_by_key(|t| t.depart);\nlet spec = PersonSpec { trips, .. };","handlingStrategy":"validation","validationCode":"person.trips.sort_by_key(|t| t.depart);","typeGuard":null,"tryCatchPattern":"match person.check_schedule() {\n    Ok(()) => {},\n    Err(e) if e.to_string().contains(\"wrong order\") => {\n        person.trips.sort_by_key(|t| t.depart);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always sort trips by departure time after import or merge","Break equal timestamps deterministically (offset by 1 second)","Call check_schedule immediately after building each PersonSpec"],"tags":["synthpop","scenario","validation"],"backgroundTag":"schema-validation-failed","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"}