a-b-street/abstreet · error

Started on a road we shouldn't trace

Error message

Started on a road we shouldn't trace

What it means

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.

Solutions

  1. Check `skip.contains(&map.get_l(start).get_nearest_side_of_road(map).road)` before calling and pick a different start lane.
  2. Choose a start lane on a road not in the skip set (iterate candidate lanes and skip filtered ones).
  3. If the road must be traced, remove that RoadID from the skip set.

Example fix

// before
let perimeter = Perimeter::single_block(map, start, &skip)?;
// after
let start_side = map.get_l(start).get_nearest_side_of_road(map);
if skip.contains(&start_side.road) {
    start = pick_lane_not_on_skipped_roads(map, &skip);
}
let perimeter = Perimeter::single_block(map, start, &skip)?;
Defensive patterns

Strategy: validation

Validate before calling

let start_side = map.get_l(start).get_nearest_side_of_road(map);
anyhow::ensure!(!skip.contains(&start_side.road), "start lane {:?} is on a skipped road", start);

Try / catch

match Perimeter::single_block(map, start, &skip) {
    Ok(p) => use(p),
    Err(_) => retry_with_different_start_lane(),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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/afa184feb0262242. Report an issue: GitHub.

Appendix: source

Thrown at blockfinding/src/lib.rs:52

// intersections or possibly by water/park areas.
#[derive(Clone, Serialize, Deserialize)]
pub struct Perimeter {
    pub roads: Vec<RoadSideID>,
    /// These roads exist entirely within the perimeter
    pub interior: BTreeSet<RoadID>,
}

impl Perimeter {
    /// Starting at any lane, snap to the nearest side of that road, then begin tracing a single
    /// block, with no interior roads. This will fail if a map boundary is reached. The results are
    /// unusual when crossing the entrance to a tunnel or bridge, and so `skip` is used to avoid
    /// tracing there.
    pub fn single_block(map: &Map, start: LaneID, skip: &HashSet<RoadID>) -> Result<Perimeter> {
        let mut roads = Vec::new();
        let start_road_side = map.get_l(start).get_nearest_side_of_road(map);

        if skip.contains(&start_road_side.road) {
            bail!("Started on a road we shouldn't trace");
        }

        // We may start on a loop road on the "inner" direction
        {
            let start_r = map.get_parent(start);
            if start_r.src_i == start_r.dst_i {
                let i = map.get_i(start_r.src_i);
                if !i.get_road_sides_sorted(map).contains(&start_road_side) {
                    bail!("Starting on inner piece of a loop road");
                }
            }
        }

        // We need to track which side of the road we're at, but also which direction we're facing
        let mut current_road_side = start_road_side;
        let mut current_intersection = map.get_l(start).dst_i;
        loop {
            let i = map.get_i(current_intersection);

View on GitHub (pinned to 0964f29315)