a-b-street/abstreet · critical

( ) is a border, but is connected to >1 road

Error message

{} ({}) is a border, but is connected to >1 road: {:?}

What it means

During map import in create_from_raw, every intersection classified as a border must connect to exactly one road. This panic fires when a border intersection is found connected to more than one road, meaning the clipping/extract-at-boundary logic produced an invalid graph that cannot be used for sim handoff at the boundary.

Solutions

  1. Adjust the clip polygon so each border intersection touches exactly one road (avoid cutting through junctions).
  2. Enlarge the extraction boundary so the problematic node becomes an interior intersection instead of a border.
  3. Inspect the printed orig_id OSM way links and fix or remove the offending ways in the raw map (via an edits JSON).
  4. Re-extract the map with the standard importer instead of hand-built RawMaps.

Example fix

// before: clip polygon corner passes through a junction, splitting one way into two segments at a border node
let polygon = Polygon::rectangle(...); // too tight
// after: extend the polygon outward so the junction is fully interior
let polygon = polygon.get_outer_shell().simplify(...).enlarge(...);
Defensive patterns

Strategy: validation

Validate before calling

// before create_from_raw: verify every border intersection has exactly one incident road
for i in &raw.intersections {
    if i.intersection_type == IntersectionType::Border {
        let n = raw.roads.iter().filter(|r| r.src_i == i.id || r.dst_i == i.id).count();
        if n > 1 { return Err(anyhow!("border {} has {} roads", i.id, n)); }
    }
}

Prevention

When it happens

Trigger: Calling Map::create_from_raw with RawMap whose clip polygon cuts an OSM way such that a border node retains two or more incident road segments (e.g. the boundary crosses the way twice near a junction).

Common situations: Importing an OSM extract with a badly shaped or too-small clip boundary; boundary running through an intersection; custom extractions made with external tools instead of the built-in extractor.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/f0dfae29764e5a84. Report an issue: GitHub.

Appendix: source

Thrown at map_model/src/make/mod.rs:218

            road.recreate_lanes(r.lane_specs_ltr.clone());
            for lane in &road.lanes {
                map.intersections[lane.src_i.0].outgoing_lanes.push(lane.id);
                map.intersections[lane.dst_i.0].incoming_lanes.push(lane.id);
            }

            map.roads.push(road);
        }

        for i in map.intersections.iter_mut() {
            if i.is_border() && i.roads.len() != 1 {
                // i.orig_id may be synthetic and useless, so also print OSM links of the roads
                let border_roads = i
                    .roads
                    .iter()
                    .map(|r| map.roads[r.0].orig_id.osm_way_id.to_string())
                    .collect::<Vec<_>>();
                panic!(
                    "{} ({}) is a border, but is connected to >1 road: {:?}",
                    i.id, i.orig_id, border_roads
                );
            }
            if i.control == IntersectionControl::Signalled {
                let mut ok = true;
                for r in &i.roads {
                    let road = &map.roads[r.0];
                    // Skip signals only connected to roads under construction or purely to control
                    // light rail tracks.
                    if road.osm_tags.is(osm::HIGHWAY, "construction") || road.is_light_rail() {
                        ok = false;
                        break;
                    }
                    // Skip signals that likely don't have correct intersection geometry
                    if road.trim_start == Distance::ZERO || road.trim_end == Distance::ZERO {
                        ok = false;
                        break;

View on GitHub (pinned to 0964f29315)