{"record":{"id":"708e05923f9111c3","repo":"a-b-street/abstreet","slug":"get-step-at-dist-along-has-leftover-distance-of","errorCode":null,"errorMessage":"get_step_at_dist_along has leftover distance of {}","messagePattern":"get_step_at_dist_along has leftover distance of (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/pathfind/v1.rs","lineNumber":556,"sourceCode":"            };\n            if from < to {\n                gain += to - from;\n            } else {\n                loss += from - to;\n            }\n        }\n        (gain, loss)\n    }\n\n    pub fn get_step_at_dist_along(&self, map: &Map, mut dist_along: Distance) -> Result<PathStep> {\n        for step in &self.steps {\n            let dist_here = self.dist_crossed_from_step(map, step);\n            if dist_along <= dist_here {\n                return Ok(*step);\n            }\n            dist_along -= dist_here;\n        }\n        bail!(\n            \"get_step_at_dist_along has leftover distance of {}\",\n            dist_along\n        );\n    }\n\n    pub fn crosses_road(&self, r: RoadID) -> bool {\n        for step in &self.steps {\n            if let PathStep::Lane(l) | PathStep::ContraflowLane(l) = step {\n                if l.road == r {\n                    return true;\n                }\n            }\n        }\n        false\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/pathfind/v1.rs#L538-L574","documentation":"get_step_at_dist_along walks a PathV1's steps, subtracting each step's crossed distance until the requested dist_along falls within a step. If the loop exhausts all steps and dist_along is still > 0, the requested distance lies beyond the path's total length, so it bails with the leftover amount. This is effectively an out-of-bounds distance lookup.","triggerScenarios":"Calling PathV1::get_step_at_dist_along with dist_along greater than the path's total length (map_model/src/pathfind/v1.rs:556), e.g. extrapolating an agent's position past the path end.","commonSituations":"Simulation ticks stepping agents farther than the remaining path length; off-by-one after slicing paths; using an unclamped distance from an animation/timeline.","solutions":["Clamp dist_along to path.total_length() (minus epsilon) before calling.","When leftover distance remains after the last step, end the trip or request a new path for the remainder.","Use dist_along = dist_along.min(self.total_length()) instead of raw values.","Audit callers that accumulate distance across ticks so they stop at path completion."],"exampleFix":"// before\nlet step = path.get_step_at_dist_along(&map, dist)?;\n// after\nlet dist = dist.min(path.total_length() - Distance::EPSILON);\nlet step = path.get_step_at_dist_along(&map, dist)?;","handlingStrategy":"validation","validationCode":"let clamped = dist.min(path.total_length() - Distance::EPSILON);\nassert!(clamped <= path.total_length());\nlet step = path.get_step_at_dist_along(&map, clamped)?;","typeGuard":null,"tryCatchPattern":"match path.get_step_at_dist_along(&map, dist) {\n    Err(e) if e.to_string().starts_with(\"get_step_at_dist_along has leftover\") => {\n        // distance beyond path end: finish the trip or fetch a new path\n    }\n    other => other?,\n}","preventionTips":["Clamp dist_along to path.total_length() before every lookup.","End agent trips when remaining distance hits the path length rather than extrapolating.","Watch for floating-point accumulation pushing dist slightly past total_length; subtract an epsilon."],"tags":["pathfinding","distance","out-of-bounds","map-model"],"backgroundTag":"value-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"}