{"record":{"id":"671abaf4dc9be7ac","repo":"a-b-street/abstreet","slug":"stop-sign-has-roads-now-but-from-edits","errorCode":null,"errorMessage":"Stop sign has {} roads now, but {} from edits","messagePattern":"Stop sign has (.+?) roads now, but (.+?) from edits","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/edits/perma.rs","lineNumber":270,"sourceCode":"                .map(|(id, turn_type)| (id.to_movement(map).to_permanent(map), *turn_type))\n                .collect(),\n        }\n    }\n}\n\nimpl PermanentEditIntersection {\n    fn with_permanent(self, i: IntersectionID, map: &Map) -> Result<EditIntersection> {\n        let control = match self.control {\n            PermanentEditIntersectionControl::StopSign { must_stop } => {\n                let mut translated_must_stop = BTreeMap::new();\n                for (r, stop) in must_stop {\n                    translated_must_stop.insert(map.find_r_by_osm_id(r)?, stop);\n                }\n\n                // Make sure the roads exactly match up\n                let mut ss = ControlStopSign::new(map, i);\n                if translated_must_stop.len() != ss.roads.len() {\n                    bail!(\n                        \"Stop sign has {} roads now, but {} from edits\",\n                        ss.roads.len(),\n                        translated_must_stop.len()\n                    );\n                }\n                for (r, stop) in translated_must_stop {\n                    if let Some(road) = ss.roads.get_mut(&r) {\n                        road.must_stop = stop;\n                    } else {\n                        bail!(\"{} doesn't connect to {}\", i, r);\n                    }\n                }\n\n                EditIntersectionControl::StopSign(ss)\n            }\n            PermanentEditIntersectionControl::TrafficSignal(ts) => {\n                EditIntersectionControl::TrafficSignal(ts)\n            }","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/edits/perma.rs#L252-L288","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet edit = EditIntersectionControl::StopSign(stop_sign_from_old_map);\nmap.maybe_edit_i(i, edit, &mut orig_edits)?;\n// after\n// rebuild the stop sign from the current intersection's roads\nlet ss = ControlStopSign::new(map, i);\nlet mut ss = ss;\nfor (r, stop) in translated_must_stop {\n    if let Some(road) = ss.roads.get_mut(&r) { road.must_stop = stop; }\n}\nmap.maybe_edit_i(i, EditIntersectionControl::StopSign(ss), &mut orig_edits)?;","handlingStrategy":"validation","validationCode":"let ss = ControlStopSign::new(&map, i);\nlet all_roads_known = edit_roads.iter().all(|r| ss.roads.contains_key(r));\nif !all_roads_known { /* repair or drop the stop-sign edit */ }","typeGuard":null,"tryCatchPattern":"match edits.to_permanent(&mut map) {\n    Ok(_) => {},\n    Err(e) if e.to_string().contains(\"roads now\") => regenerate_stop_sign_edits(&map),\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["map-edits","stop-sign","stale-edit"],"backgroundTag":"invalid-state-transition","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}