{"record":{"id":"7c298c137e047620","repo":"a-b-street/abstreet","slug":"negative-dist-ahead","errorCode":null,"errorMessage":"Negative dist_ahead?! {}","messagePattern":"Negative dist_ahead\\?! (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/pathfind/v1.rs","lineNumber":53,"sourceCode":"    pub fn as_lane(&self) -> LaneID {\n        self.as_traversable().as_lane()\n    }\n\n    pub fn as_turn(&self) -> TurnID {\n        self.as_traversable().as_turn()\n    }\n\n    // start is relative to the start of the actual geometry -- so from the lane's real start for\n    // ContraflowLane.\n    fn exact_slice(\n        &self,\n        map: &Map,\n        start: Distance,\n        dist_ahead: Option<Distance>,\n    ) -> Result<PolyLine> {\n        if let Some(d) = dist_ahead {\n            if d < Distance::ZERO {\n                panic!(\"Negative dist_ahead?! {}\", d);\n            }\n            if d == Distance::ZERO {\n                bail!(\"0 dist ahead for slice\");\n            }\n        }\n\n        match self {\n            PathStep::Lane(id) => {\n                let pts = &map.get_l(*id).lane_center_pts;\n                if let Some(d) = dist_ahead {\n                    pts.maybe_exact_slice(start, start + d)\n                } else {\n                    pts.maybe_exact_slice(start, pts.length())\n                }\n            }\n            PathStep::ContraflowLane(id) => {\n                let pts = map.get_l(*id).lane_center_pts.reversed();\n                let reversed_start = pts.length() - start;","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/pathfind/v1.rs#L35-L71","documentation":"Path::exact_slice slices a path to a requested distance ahead; a negative dist_ahead is meaningless, so the method panics with \"Negative dist_ahead?!\". Zero distance is handled separately as a normal Err. This is a caller contract check: distances passed for slicing must be non-negative.","triggerScenarios":"Calling exact_slice with dist_ahead = Some(negative Distance), typically from an arithmetic result that underflowed (e.g. remaining_distance - dist_already_traveled going below zero).","commonSituations":"Slice/trim logic that computes the lookahead distance by subtraction and can go negative at path endpoints; sim agents requesting a slice past the end of a route with signed math.","solutions":["Clamp the distance before calling: Some(d.max(Distance::ZERO))","Skip the slice call when the computed remaining distance is <= zero","Fix the upstream arithmetic that produced the negative distance (usually a subtraction without a floor)","Pass None when you do not need a bounded slice"],"exampleFix":"// before\nlet slice = path.exact_slice(map, start, Some(remaining))?,;\n// after\nlet d = remaining.max(Distance::ZERO);\nlet slice = if d == Distance::ZERO { None } else { Some(path.exact_slice(map, start, Some(d))?) };","handlingStrategy":"validation","validationCode":"fn safe_dist_ahead(d: Distance) -> Option<Distance> {\n    if d < Distance::ZERO { None } else { Some(d) }\n}","typeGuard":"fn non_negative(d: Distance) -> Option<Distance> {\n    if d >= Distance::ZERO { Some(d) } else { None }\n}","tryCatchPattern":"// method returns Result and only panics on negatives; clamp first:\nmatch path.exact_slice(map, start, Some(d.max(Distance::ZERO))) {\n    Ok(slice) => handle(slice),\n    Err(e) => handle_zero_slice(e), // catches \"0 dist ahead\" via bail!\n}","preventionTips":["Clamp any computed lookahead distance with max(Distance::ZERO)","Handle the zero-distance Err case explicitly (it returns Err, not panic)","Avoid subtraction-based distances without a floor","Pass None when the slice bound is not needed"],"tags":["rust","panic","invalid-argument","pathfinding"],"backgroundTag":"argument-out-of-range","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"}