a-b-street/abstreet · error

doesn't have an endpoint at

Error message

{} doesn't have an endpoint at {}

What it means

Road::incoming_lanes returns the lanes whose traffic enters the given intersection along this road: children_backwards if i is the source, children_forwards if i is the destination. Panics when i isn't an endpoint, since there's no valid direction into it.

Solutions

  1. Assert src_i == i || dst_i == i before calling, or derive i from the road's endpoints.
  2. Fix the caller's adjacency iteration so only roads incident to i are processed.
  3. Re-lookup IDs against the current map rather than reusing persisted ones.

Example fix

// before
let lanes = road.incoming_lanes(i);
// after
if road.src_i != i && road.dst_i != i { anyhow::bail!("road {} has no endpoint at {}", road.id, i); }
let lanes = road.incoming_lanes(i);
Defensive patterns

Strategy: type-guard

Type guard

fn incoming_lanes_safe(road: &Road, i: IntersectionID) -> Option<Vec<(LaneID, LaneType)>> {
    if road.src_i == i { Some(road.children_backwards()) }
    else if road.dst_i == i { Some(road.children_forwards()) }
    else { None }
}

Prevention

When it happens

Trigger: Calling road.incoming_lanes(i) with an intersection that is neither src_i nor dst_i — usually in movement/turn generation or signal-building code that iterates the wrong intersection set.

Common situations: Intersection geometry and traffic-signal generation during import; custom movement queries; stale intersection IDs after re-import.

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


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/3d7e3862b658ad12. Report an issue: GitHub.

Appendix: source

Thrown at map_model/src/objects/road.rs:698

    }

    // TODO Deprecated
    pub(crate) fn children(&self, dir: Direction) -> Vec<(LaneID, LaneType)> {
        if dir == Direction::Fwd {
            self.children_forwards()
        } else {
            self.children_backwards()
        }
    }

    /// Returns lanes from the "center" going out
    pub(crate) fn incoming_lanes(&self, i: IntersectionID) -> Vec<(LaneID, LaneType)> {
        if self.src_i == i {
            self.children_backwards()
        } else if self.dst_i == i {
            self.children_forwards()
        } else {
            panic!("{} doesn't have an endpoint at {}", self.id, i);
        }
    }
}

/// Refers to a road segment between two nodes, using OSM IDs. Note OSM IDs are not stable over
/// time and the relationship between a road/intersection and way/node isn't 1:1 at all.
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OriginalRoad {
    pub osm_way_id: osm::WayID,
    pub i1: osm::NodeID,
    pub i2: osm::NodeID,
}

impl fmt::Display for OriginalRoad {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "OriginalRoad({} from {} to {}",

View on GitHub (pinned to 0964f29315)