a-b-street/abstreet · error
speed limit has changed
Error message
{:?} speed limit has changed What it means
While upgrading old edits, a ChangeSpeedLimit command records the previous speed limit (obj.old). The upgrade verifies it still matches the road's current speed_limit; if not, the edit was based on a state that no longer exists, so it aborts rather than applying a change with the wrong baseline.
Solutions
- Update the command's `old` field in the edit JSON to the road's current speed limit.
- Remove the ChangeSpeedLimit command and re-apply the speed limit change in the app.
- Regenerate the map so the speed limit matches the edit's recorded old value.
Example fix
// before (edit JSON)
{"ChangeSpeedLimit": {"id": 42, "old": 30.0, "new": 20.0}}
// after: update `old` to the current limit
{"ChangeSpeedLimit": {"id": 42, "old": 25.0, "new": 20.0}} Defensive patterns
Strategy: validation
Validate before calling
// confirm the recorded old speed limit matches the current road
let r = map.find_r_by_osm_id(cmd.id)?;
if map.get_r_edit(r).speed_limit != cmd.old { /* update `old` or drop cmd */ } Try / catch
match Edits::load_from_bytes(map, bytes) {
Ok(e) => Ok(e),
Err(err) if err.to_string().contains("speed limit has changed") => {
Err(anyhow!("edit baseline stale: {}", err))
}
Err(err) => Err(err),
} Prevention
- After OSM re-import, refresh the `old` fields in saved edits or re-create them.
- Expect default speed limits to change between map builds; diff before replaying.
- Keep edits tied to a specific map version.
When it happens
Trigger: Loading a saved edit whose ChangeSpeedLimit.old differs from road.speed_limit in the current map — e.g. the basemap was regenerated (different OSM-derived defaults) or the limit was already changed.
Common situations: Re-importing a map after OSM data updates changed default speed limits; loading edits saved against an older map build; applying an edit file twice.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- lane type has changed
- access restrictions have changed
- 's road doesn't point to dst_i at all
- 's road already points to dst_i
- number of lanes in is ( fwd, back) now, but ( , ) in the…
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/9925722aabd5b03c.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/edits/compat.rs:249
let dst_i = map.find_i_by_osm_id(obj.dst_i)?;
let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
let edits_dir = if dst_i == map.get_r(r).dst_i {
Direction::Fwd
} else if dst_i == map.get_r(r).src_i {
Direction::Back
} else {
bail!("{:?}'s road doesn't point to dst_i at all", obj);
};
if road.lanes_ltr[idx].dir == edits_dir {
bail!("{:?}'s road already points to dst_i", obj);
}
road.lanes_ltr[idx].dir = edits_dir;
} else if let Some(obj) = cmd.remove("ChangeSpeedLimit") {
let obj: ChangeSpeedLimit = serde_json::from_value(obj).unwrap();
let r = map.find_r_by_osm_id(obj.id)?;
let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
if road.speed_limit != obj.old {
bail!("{:?} speed limit has changed", obj);
}
road.speed_limit = obj.new;
} else if let Some(obj) = cmd.remove("ChangeAccessRestrictions") {
let obj: ChangeAccessRestrictions = serde_json::from_value(obj).unwrap();
let r = map.find_r_by_osm_id(obj.id)?;
let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
if road.access_restrictions != obj.old {
bail!("{:?} access restrictions have changed", obj);
}
road.access_restrictions = obj.new.clone();
} else {
commands.push(orig);
}
}
for (r, new) in modified {
let old = map.get_r_edit(r);
commandsView on GitHub (pinned to 0964f29315)