a-b-street/abstreet · error

Couldn't find a border near end

Error message

Couldn't find a {:?} border near end {}

What it means

Symmetric to the start: when a route ends off-map, the last stop's driving position must be near an outgoing border of the route's vehicle type. If no such border is within the snap threshold, the route's exit can't be established and create_route bails.

Solutions

  1. Increase border_snap_threshold so the exit point snaps to the nearest border.
  2. Verify outgoing borders for the route's vehicle type exist near the route end; check map border generation.
  3. Trim the route shape/stops so the route ends inside the map boundary.
Defensive patterns

Strategy: try-catch

Validate before calling

if !map.boundary_polygon.contains_pt(route.shape.last_pt()) {
    let near = borders.closest_pt(exit_pt, border_snap_threshold);
    if near.is_none() { /* trim route or widen threshold */ }
}

Try / catch

match create_route(...) {
    Err(e) if e.to_string().contains("border near end") => {
        warn!("route {} ends off-map", route.gtfs_id); skip();
    }
    other => other?,
}

Prevention

When it happens

Trigger: finalize_transit -> create_route where route.shape.last_pt() is outside map.boundary_polygon and the FindClosest lookup for outgoing Bus/Train borders around exit_pt finds nothing within border_snap_threshold (30m).

Common situations: Routes terminating outside the imported area; the nearest border is the wrong vehicle type; GTFS route shapes extending beyond the clipped map with the last stop inside.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at map_model/src/make/transit.rs:214

            .ok_or_else(|| anyhow!("couldn't find where shape leaves map"))?;
        // Snap that to a border
        let borders = if route.route_type == RawTransitType::Bus {
            &snapper.bus_outgoing_borders
        } else {
            &snapper.train_outgoing_borders
        };
        match borders.closest_pt(exit_pt, border_snap_threshold) {
            Some((lane, _)) => {
                // Edge case: the last stop is on the same road as the border. We can't lane-change
                // suddenly, so match the lane in that case.
                let last_stop_lane = map.get_ts(*stops.last().unwrap()).driving_pos.lane();
                Some(if lane.road == last_stop_lane.road {
                    last_stop_lane
                } else {
                    lane
                })
            }
            None => bail!(
                "Couldn't find a {:?} border near end {}",
                route.route_type,
                exit_pt
            ),
        }
    };

    // TODO This'll come from the RawTransitRoute eventually. For now, every 30 minutes.
    let spawn_times: Vec<Time> = (0..48)
        .map(|i| Time::START_OF_DAY + (i as f64) * Duration::minutes(30))
        .collect();

    let result = TransitRoute {
        id: TransitRouteID(map.transit_routes.len()),
        long_name: route.long_name.clone(),
        short_name: route.short_name.clone(),
        gtfs_id: route.gtfs_id.clone(),
        stops,

View on GitHub (pinned to 0964f29315)