a-b-street/abstreet · error
doesn't contain
Error message
{} doesn't contain {} What it means
Road::dir_and_offset scans both directions' child lane lists for the given LaneID to return its direction and index. Panics when the road contains no such lane, i.e. the lane belongs to a different road.
Solutions
- Verify lane belongs to the road: road.children(Direction::Fwd).iter().chain(children(Back)) contains the ID before calling.
- Recompute the LaneID from the same road reference rather than caching it.
- Check that edits/persisted IDs match the current map version; regenerate or re-target by orig_id.
Example fix
// before
let (dir, off) = road.dir_and_offset(lane);
// after
if !road.children(Direction::Fwd).iter().chain(road.children(Direction::Back).iter()).any(|(l, _)| *l == lane) {
anyhow::bail!("{} not in {}", lane, road.id);
}
let (dir, off) = road.dir_and_offset(lane); Defensive patterns
Strategy: type-guard
Type guard
fn dir_and_offset_safe(road: &Road, lane: LaneID) -> Option<(Direction, usize)> {
for dir in [Direction::Fwd, Direction::Back] {
if let Some(idx) = road.children(dir).iter().position(|p| p.0 == lane) {
return Some((dir, idx));
}
}
None
} Prevention
- Only pass lane IDs obtained from the same Road object
- Re-target persisted edit lane IDs by orig_id after re-import
- Unit-test lane-offset helpers against regenerated maps
When it happens
Trigger: Calling road.dir_and_offset(lane) with a LaneID that is not a child of that road — e.g. derived from a different road, or from a stale/invalid ID after edits or re-import.
Common situations: Lane-offset math in edits and drawing code; mixing up lane IDs between two adjacent roads; loading edits JSON against a regenerated map where IDs shifted.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- isn't an endpoint of
- doesn't touch
- must_get_sidewalk broken by
- doesn't point to
- doesn't contain both and
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/b8db3ac41c86254e.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/objects/road.rs:239
allowed_turns: Default::default(),
})
.collect()
}
pub fn shift_from_left_side(&self, width_from_left_side: Distance) -> Result<PolyLine> {
self.center_pts
.shift_from_center(self.get_width(), width_from_left_side)
}
/// lane must belong to this road. Offset 0 is the centermost lane on each side of a road, then
/// it counts up from there. Note this is a different offset than `offset`!
pub(crate) fn dir_and_offset(&self, lane: LaneID) -> (Direction, usize) {
for dir in [Direction::Fwd, Direction::Back] {
if let Some(idx) = self.children(dir).iter().position(|pair| pair.0 == lane) {
return (dir, idx);
}
}
panic!("{} doesn't contain {}", self.id, lane);
}
pub fn parking_to_driving(&self, parking: LaneID) -> Option<LaneID> {
self.find_closest_lane(parking, |l| l.is_driving())
}
pub(crate) fn speed_limit_from_osm(&self) -> Speed {
if let Some(limit) = self.osm_tags.get("maxspeed") {
if let Some(speed) = if let Ok(kmph) = limit.parse::<f64>() {
Some(Speed::km_per_hour(kmph))
} else if let Some(mph) = limit
.strip_suffix(" mph")
.and_then(|x| x.parse::<f64>().ok())
{
Some(Speed::miles_per_hour(mph))
} else {
None
} {View on GitHub (pinned to 0964f29315)