a-b-street/abstreet · error
get_step_at_dist_along has leftover distance of
Error message
get_step_at_dist_along has leftover distance of {} What it means
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.
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.
Example fix
// before let step = path.get_step_at_dist_along(&map, dist)?; // after let dist = dist.min(path.total_length() - Distance::EPSILON); let step = path.get_step_at_dist_along(&map, dist)?;
Defensive patterns
Strategy: validation
Validate before calling
let clamped = dist.min(path.total_length() - Distance::EPSILON); assert!(clamped <= path.total_length()); let step = path.get_step_at_dist_along(&map, clamped)?;
Try / catch
match path.get_step_at_dist_along(&map, dist) {
Err(e) if e.to_string().starts_with("get_step_at_dist_along has leftover") => {
// distance beyond path end: finish the trip or fetch a new path
}
other => other?,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Simulation ticks stepping agents farther than the remaining path length; off-by-one after slicing paths; using an unclamped distance from an animation/timeline.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- 0 dist ahead for slice
- Can't transform a road-based path to a lane-based path for
- doesn't point to
- PathConstraints::from_lt
- Negative dist_ahead?!
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/708e05923f9111c3.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/pathfind/v1.rs:556
};
if from < to {
gain += to - from;
} else {
loss += from - to;
}
}
(gain, loss)
}
pub fn get_step_at_dist_along(&self, map: &Map, mut dist_along: Distance) -> Result<PathStep> {
for step in &self.steps {
let dist_here = self.dist_crossed_from_step(map, step);
if dist_along <= dist_here {
return Ok(*step);
}
dist_along -= dist_here;
}
bail!(
"get_step_at_dist_along has leftover distance of {}",
dist_along
);
}
pub fn crosses_road(&self, r: RoadID) -> bool {
for step in &self.steps {
if let PathStep::Lane(l) | PathStep::ContraflowLane(l) = step {
if l.road == r {
return true;
}
}
}
false
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]View on GitHub (pinned to 0964f29315)