{"record":{"id":"8649626a1957e46c","repo":"a-b-street/abstreet","slug":"starting-on-inner-piece-of-a-loop-road","errorCode":null,"errorMessage":"Starting on inner piece of a loop road","messagePattern":"Starting on inner piece of a loop road","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"blockfinding/src/lib.rs","lineNumber":61,"sourceCode":"    /// Starting at any lane, snap to the nearest side of that road, then begin tracing a single\n    /// block, with no interior roads. This will fail if a map boundary is reached. The results are\n    /// unusual when crossing the entrance to a tunnel or bridge, and so `skip` is used to avoid\n    /// tracing there.\n    pub fn single_block(map: &Map, start: LaneID, skip: &HashSet<RoadID>) -> Result<Perimeter> {\n        let mut roads = Vec::new();\n        let start_road_side = map.get_l(start).get_nearest_side_of_road(map);\n\n        if skip.contains(&start_road_side.road) {\n            bail!(\"Started on a road we shouldn't trace\");\n        }\n\n        // We may start on a loop road on the \"inner\" direction\n        {\n            let start_r = map.get_parent(start);\n            if start_r.src_i == start_r.dst_i {\n                let i = map.get_i(start_r.src_i);\n                if !i.get_road_sides_sorted(map).contains(&start_road_side) {\n                    bail!(\"Starting on inner piece of a loop road\");\n                }\n            }\n        }\n\n        // We need to track which side of the road we're at, but also which direction we're facing\n        let mut current_road_side = start_road_side;\n        let mut current_intersection = map.get_l(start).dst_i;\n        loop {\n            let i = map.get_i(current_intersection);\n            if i.is_border() {\n                bail!(\"hit the map boundary at {}\", i.orig_id);\n            }\n            let mut sorted_roads = i.get_road_sides_sorted(map);\n            sorted_roads.retain(|id| !skip.contains(&id.road));\n\n            let idx = sorted_roads\n                .iter()\n                .position(|x| *x == current_road_side)","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/blockfinding/src/lib.rs#L43-L79","documentation":"The start lane is on a loop road (a road whose src and dst intersection are the same), but on the inner direction that isn't represented among the intersection's sorted road sides. Tracing from such a lane cannot produce a valid perimeter, so the library refuses the start point.","triggerScenarios":"Calling Perimeter::single_block with a start lane whose parent road is a loop (get_parent(start).src_i == dst_i) and where i.get_road_sides_sorted(map) does not contain the lane's nearest road side.","commonSituations":"Roundabouts and loop ramps in OSM data frequently produce one-way loop roads; picking an arbitrary lane on such a road as the trace start hits the inner direction.","solutions":["Pick a different lane on the loop road whose side appears in the intersection's sorted road sides.","Add the loop road to the `skip` set and start tracing elsewhere.","If you must trace loops, handle them specially outside single_block (the library explicitly doesn't support inner loop starts)."],"exampleFix":"// before\nlet perimeter = Perimeter::single_block(map, some_lane_on_loop, &skip)?;\n// after\nlet r = map.get_parent(some_lane_on_loop);\nif r.src_i == r.dst_i {\n    some_lane_on_loop = pick_outer_loop_lane(map, r.id); // side present in sorted road sides\n}\nlet perimeter = Perimeter::single_block(map, some_lane_on_loop, &skip)?;","handlingStrategy":"validation","validationCode":"let r = map.get_parent(start);\nif r.src_i == r.dst_i {\n    let i = map.get_i(r.src_i);\n    let side = map.get_l(start).get_nearest_side_of_road(map);\n    anyhow::ensure!(i.get_road_sides_sorted(map).contains(&side), \"inner loop start\");\n}","typeGuard":"fn is_inner_loop_start(map: &Map, start: LaneID) -> bool {\n    let r = map.get_parent(start);\n    if r.src_i != r.dst_i { return false; }\n    let side = map.get_l(start).get_nearest_side_of_road(map);\n    !map.get_i(r.src_i).get_road_sides_sorted(map).contains(&side)\n}","tryCatchPattern":null,"preventionTips":["Detect loop roads (src_i == dst_i) before choosing start lanes.","Prefer outer-direction lanes on roundabouts/loop ramps."],"tags":["loop-road","perimeter-tracing"],"backgroundTag":"unsupported-operation","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"}