{"record":{"id":"3d7e3862b658ad12","repo":"a-b-street/abstreet","slug":"doesn-t-have-an-endpoint-at","errorCode":null,"errorMessage":"{} doesn't have an endpoint at {}","messagePattern":"(.+?) doesn't have an endpoint at (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/objects/road.rs","lineNumber":698,"sourceCode":"    }\n\n    // TODO Deprecated\n    pub(crate) fn children(&self, dir: Direction) -> Vec<(LaneID, LaneType)> {\n        if dir == Direction::Fwd {\n            self.children_forwards()\n        } else {\n            self.children_backwards()\n        }\n    }\n\n    /// Returns lanes from the \"center\" going out\n    pub(crate) fn incoming_lanes(&self, i: IntersectionID) -> Vec<(LaneID, LaneType)> {\n        if self.src_i == i {\n            self.children_backwards()\n        } else if self.dst_i == i {\n            self.children_forwards()\n        } else {\n            panic!(\"{} doesn't have an endpoint at {}\", self.id, i);\n        }\n    }\n}\n\n/// Refers to a road segment between two nodes, using OSM IDs. Note OSM IDs are not stable over\n/// time and the relationship between a road/intersection and way/node isn't 1:1 at all.\n#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct OriginalRoad {\n    pub osm_way_id: osm::WayID,\n    pub i1: osm::NodeID,\n    pub i2: osm::NodeID,\n}\n\nimpl fmt::Display for OriginalRoad {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(\n            f,\n            \"OriginalRoad({} from {} to {}\",","sourceCodeStart":680,"sourceCodeEnd":716,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/objects/road.rs#L680-L716","documentation":"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.","triggerScenarios":"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.","commonSituations":"Intersection geometry and traffic-signal generation during import; custom movement queries; stale intersection IDs after re-import.","solutions":["Assert src_i == i || dst_i == i before calling, or derive i from the road's endpoints.","Fix the caller's adjacency iteration so only roads incident to i are processed.","Re-lookup IDs against the current map rather than reusing persisted ones."],"exampleFix":"// before\nlet lanes = road.incoming_lanes(i);\n// after\nif road.src_i != i && road.dst_i != i { anyhow::bail!(\"road {} has no endpoint at {}\", road.id, i); }\nlet lanes = road.incoming_lanes(i);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn incoming_lanes_safe(road: &Road, i: IntersectionID) -> Option<Vec<(LaneID, LaneType)>> {\n    if road.src_i == i { Some(road.children_backwards()) }\n    else if road.dst_i == i { Some(road.children_forwards()) }\n    else { None }\n}","tryCatchPattern":null,"preventionTips":["Only iterate (road, intersection) pairs where the road is incident to the intersection","Prefer children_forwards/backwards directly when direction is known","Re-lookup intersection IDs after any re-import or edit application"],"tags":["graph","invariant","wrong-argument","intersections"],"backgroundTag":"invalid-argument-value","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"}