a-b-street/abstreet · error
Stop sign has roads now, but from edits
Error message
Stop sign has {} roads now, but {} from edits What it means
During permanent-edit application, every road adjacent to a stop-sign intersection that was listed in the edit must still exist on the intersection after permanent map changes are applied. ControlStopSign::new rebuilds the sign's road set from the current map; if the count of edited roads differs from the rebuilt set's size, some edited road is no longer adjacent, and the library bails instead of silently dropping edits.
Solutions
- Re-save the stop-sign edit against the current map: open the edits in the UI and re-confirm the stop sign so the road list matches the intersection's current roads.
- Check whether the permanent edits changed the intersection's roads (deleted/merged roads) and remove stale entries from the edit's stop-sign road list.
- Regenerate the map and recreate the edit if the underlying OSM data changed significantly.
Example fix
// before
let edit = EditIntersectionControl::StopSign(stop_sign_from_old_map);
map.maybe_edit_i(i, edit, &mut orig_edits)?;
// after
// rebuild the stop sign from the current intersection's roads
let ss = ControlStopSign::new(map, i);
let mut ss = ss;
for (r, stop) in translated_must_stop {
if let Some(road) = ss.roads.get_mut(&r) { road.must_stop = stop; }
}
map.maybe_edit_i(i, EditIntersectionControl::StopSign(ss), &mut orig_edits)?; Defensive patterns
Strategy: validation
Validate before calling
let ss = ControlStopSign::new(&map, i);
let all_roads_known = edit_roads.iter().all(|r| ss.roads.contains_key(r));
if !all_roads_known { /* repair or drop the stop-sign edit */ } Try / catch
match edits.to_permanent(&mut map) {
Ok(_) => {},
Err(e) if e.to_string().contains("roads now") => regenerate_stop_sign_edits(&map),
Err(e) => return Err(e),
} Prevention
- Regenerate edits against the same map version they were created from
- Before applying edits, validate every road id in stop-sign edits exists on the target intersection
- Keep edits and map builds in sync in version control
When it happens
Trigger: Calling Edits::to_permanent / with_permanent on an edit containing an EditIntersectionControl::StopSign whose translated_must_stop map references a road that, after the permanent edit was applied (e.g. a road was deleted or merged), is no longer connected to the intersection.
Common situations: Applying an edit JSON made against an older/modified map where the stop-sign intersection's geometry changed; editing a save file after a map regeneration; merging edits that delete roads adjacent to stop signs.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- doesn't connect to
- must_get_sidewalk broken by
- doesn't contain both and
- Breaking changes happened to map edits between v12 and v13…
- didn't map to exactly 1 crossing turn
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/671abaf4dc9be7ac.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/edits/perma.rs:270
.map(|(id, turn_type)| (id.to_movement(map).to_permanent(map), *turn_type))
.collect(),
}
}
}
impl PermanentEditIntersection {
fn with_permanent(self, i: IntersectionID, map: &Map) -> Result<EditIntersection> {
let control = match self.control {
PermanentEditIntersectionControl::StopSign { must_stop } => {
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)
}View on GitHub (pinned to 0964f29315)