{"record":{"id":"afa184feb0262242","repo":"a-b-street/abstreet","slug":"started-on-a-road-we-shouldn-t-trace","errorCode":null,"errorMessage":"Started on a road we shouldn't trace","messagePattern":"Started on a road we shouldn't trace","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"blockfinding/src/lib.rs","lineNumber":52,"sourceCode":"// intersections or possibly by water/park areas.\n#[derive(Clone, Serialize, Deserialize)]\npub struct Perimeter {\n    pub roads: Vec<RoadSideID>,\n    /// These roads exist entirely within the perimeter\n    pub interior: BTreeSet<RoadID>,\n}\n\nimpl Perimeter {\n    /// 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);","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/blockfinding/src/lib.rs#L34-L70","documentation":"Perimeter::single_block traces a block perimeter starting from a lane, but the caller supplied a `skip` set and the starting lane's road is in it. The library refuses to begin tracing a road it was explicitly told to avoid (typically bridges/tunnels), so it bails immediately rather than produce a wrong perimeter.","triggerScenarios":"Calling Perimeter::single_block(map, start_lane, &skip) where the road containing `start_lane` is a member of `skip` — i.e. the chosen start lane's nearest road side is one of the roads the caller excluded from tracing.","commonSituations":"FindBlocks passes a precomputed skip set (tunnels, bridges, tagged untraced roads) and the randomly or externally chosen start lane happens to lie on one of those roads; callers reusing a start lane from a previous run after the skip set changed.","solutions":["Check `skip.contains(&map.get_l(start).get_nearest_side_of_road(map).road)` before calling and pick a different start lane.","Choose a start lane on a road not in the skip set (iterate candidate lanes and skip filtered ones).","If the road must be traced, remove that RoadID from the skip set."],"exampleFix":"// before\nlet perimeter = Perimeter::single_block(map, start, &skip)?;\n// after\nlet start_side = map.get_l(start).get_nearest_side_of_road(map);\nif skip.contains(&start_side.road) {\n    start = pick_lane_not_on_skipped_roads(map, &skip);\n}\nlet perimeter = Perimeter::single_block(map, start, &skip)?;","handlingStrategy":"validation","validationCode":"let start_side = map.get_l(start).get_nearest_side_of_road(map);\nanyhow::ensure!(!skip.contains(&start_side.road), \"start lane {:?} is on a skipped road\", start);","typeGuard":null,"tryCatchPattern":"match Perimeter::single_block(map, start, &skip) {\n    Ok(p) => use(p),\n    Err(_) => retry_with_different_start_lane(),\n}","preventionTips":["Pick start lanes from roads already filtered against the skip set.","When reusing start lanes from cached runs, revalidate against the current skip set."],"tags":["perimeter-tracing","input-validation"],"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"}