a-b-street/abstreet · error

doesn't connect to

Error message

{} doesn't connect to {}

What it means

After rebuilding a stop sign for a permanent edit, each edited road id must exist in the new ControlStopSign's road set. If a road referenced by the edit is not adjacent to the intersection anymore, the edit cannot be applied and the library bails naming the intersection and the road id.

Solutions

  1. Remove the stale road entry from the stop-sign edit (verify the road id exists on that intersection in the current map).
  2. Recreate the stop-sign edit on the current map version via the UI or edits tooling.
  3. If the road id was renumbered by regeneration, remap the edit with the map's id-mapping before applying.

Example fix

// before (edit references road that no longer touches the intersection)
bail!("{} doesn't connect to {}", i, r);
// after (skip/repair stale roads before applying)
let ss = ControlStopSign::new(map, i);
for (r, stop) in translated_must_stop {
    if ss.roads.contains_key(&r) {
        ss.roads.get_mut(&r).unwrap().must_stop = stop;
    }
}
Defensive patterns

Strategy: validation

Validate before calling

for (r, _) in &translated_must_stop {
    assert!(map.get_i(i).roads.contains(r), "road {:?} no longer touches {:?}", r, i);
}

Try / catch

match with_permanent(...) {
    Err(e) if e.to_string().contains("doesn't connect to") => {
        // drop the stale stop-sign edit and re-record it
    }
    other => other?,
}

Prevention

When it happens

Trigger: with_permanent applying EditIntersectionControl::StopSign where translated_must_stop contains a RoadID not present in ss.roads — typically because the permanent edits deleted, merged, or renumbered roads touching that intersection.

Common situations: Applying an old edits.json to a regenerated map; layering edits on top of permanent edits that changed road topology; sharing edits between slightly different map builds.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at map_model/src/edits/perma.rs:280

                let mut translated_must_stop = BTreeMap::new();
                for (r, stop) in must_stop {
                    translated_must_stop.insert(map.find_r_by_osm_id(r)?, stop);
                }

                // Make sure the roads exactly match up
                let mut ss = ControlStopSign::new(map, i);
                if translated_must_stop.len() != ss.roads.len() {
                    bail!(
                        "Stop sign has {} roads now, but {} from edits",
                        ss.roads.len(),
                        translated_must_stop.len()
                    );
                }
                for (r, stop) in translated_must_stop {
                    if let Some(road) = ss.roads.get_mut(&r) {
                        road.must_stop = stop;
                    } else {
                        bail!("{} doesn't connect to {}", i, r);
                    }
                }

                EditIntersectionControl::StopSign(ss)
            }
            PermanentEditIntersectionControl::TrafficSignal(ts) => {
                EditIntersectionControl::TrafficSignal(ts)
            }
            PermanentEditIntersectionControl::Closed => EditIntersectionControl::Closed,
        };

        let mut crosswalks = BTreeMap::new();
        for (id, turn_type) in self.crosswalks {
            let movement = MovementID::from_permanent(id, map)?;
            // Find all TurnIDs that map to this MovementID
            let mut turn_ids = Vec::new();
            for turn in &map.get_i(i).turns {
                if turn.id.to_movement(map) == movement {

View on GitHub (pinned to 0964f29315)