{"record":{"id":"444ee56813235324","repo":"a-b-street/abstreet","slug":"isn-t-an-endpoint-of","errorCode":null,"errorMessage":"{} isn't an endpoint of {}","messagePattern":"(.+?) isn't an endpoint of (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/objects/lane.rs","lineNumber":114,"sourceCode":"        self.lane_center_pts.first_pt()\n    }\n    pub fn last_pt(&self) -> Pt2D {\n        self.lane_center_pts.last_pt()\n    }\n    pub fn first_line(&self) -> Line {\n        self.lane_center_pts.first_line()\n    }\n    pub fn last_line(&self) -> Line {\n        self.lane_center_pts.last_line()\n    }\n\n    pub fn endpoint(&self, i: IntersectionID) -> Pt2D {\n        if i == self.src_i {\n            self.first_pt()\n        } else if i == self.dst_i {\n            self.last_pt()\n        } else {\n            panic!(\"{} isn't an endpoint of {}\", i, self.id);\n        }\n    }\n\n    /// pt2 will be endpoint\n    pub fn end_line(&self, i: IntersectionID) -> Line {\n        if i == self.src_i {\n            self.first_line().reversed()\n        } else if i == self.dst_i {\n            self.last_line()\n        } else {\n            panic!(\"{} isn't an endpoint of {}\", i, self.id);\n        }\n    }\n\n    pub fn dist_along_of_point(&self, pt: Pt2D) -> Option<Distance> {\n        self.lane_center_pts\n            .dist_along_of_point(pt)\n            .map(|(dist, _)| dist)","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/objects/lane.rs#L96-L132","documentation":"Lane::endpoint returns the first or last point of the lane depending on which intersection is requested. Panics when the given IntersectionID is neither the lane's source nor destination intersection, i.e. the lane does not touch that intersection.","triggerScenarios":"Calling lane.endpoint(i) with an IntersectionID that is not lane.src_i or lane.dst_i — e.g. passing the wrong intersection from a turn-building loop or using a stale ID after edits/regeneration.","commonSituations":"Crosswalk or shared-sidewalk-corner construction (make_crosswalk, make_shared_sidewalk_corner) iterating wrong lane sets; using lane IDs from an old map after re-import, since IDs are not stable.","solutions":["Verify the intersection is an endpoint: check lane.src_i or lane.dst_i before calling endpoint.","Use lane.src_i or lane.dst_i directly instead of an assumed intersection.","Do not persist LaneID/IntersectionID across map reloads; look them up by stable identifiers each time.","Check caller logic (crosswalk generation, edits) that pairs lanes with intersections."],"exampleFix":"// before\nlet pt = lane.endpoint(some_i);\n// after\nassert!(lane.src_i == some_i || lane.dst_i == some_i, \"lane {} not at {}\", lane.id, some_i);\nlet pt = lane.endpoint(some_i);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn lane_endpoint_ok(lane: &Lane, i: IntersectionID) -> Option<Pt2D> {\n    if i == lane.src_i { Some(lane.first_pt()) }\n    else if i == lane.dst_i { Some(lane.last_pt()) }\n    else { None }\n}","tryCatchPattern":null,"preventionTips":["Never cache LaneID/IntersectionID across map reloads","Derive intersections from lane.src_i/dst_i instead of parallel loops","Use Option-returning helpers instead of panicking APIs in new code"],"tags":["geometry","invariant","wrong-argument","lanes"],"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"}