a-b-street/abstreet · error

's road doesn't point to dst_i at all

Error message

{:?}'s road doesn't point to dst_i at all

What it means

During compat upgrade of a ReverseLane command, the code determines which direction the edited road must point by comparing dst_i with the road's src_i/dst_i. If the destination intersection is not an endpoint of the road at all, the command's assumptions are broken relative to the current map and upgrade bails.

Solutions

  1. Remove the stale ReverseLane command from the edit JSON and re-apply the change manually.
  2. Regenerate or use the map version the edit was created against.
  3. Update the command's road/intersection IDs to match the current map topology.
Defensive patterns

Strategy: validation

Validate before calling

// verify dst_i is an endpoint of the road before loading
let r = map.get_r(map.find_r_by_osm_id(cmd_id)?);
let ok = cmd.dst_i == r.src_i || cmd.dst_i == r.dst_i;

Prevention

When it happens

Trigger: Loading an old edit file whose ReverseLane references a road whose endpoints changed (road split/merged after map regeneration), so obj.dst_i matches neither get_r(r).dst_i nor src_i.

Common situations: Applying edits saved months ago to a freshly regenerated map where OSM geometry changes split or rewired roads; replaying edits across map versions.

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


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

Appendix: source

Thrown at map_model/src/edits/compat.rs:238

        if let Some(obj) = cmd.remove("ChangeLaneType") {
            let obj: ChangeLaneType = serde_json::from_value(obj).unwrap();
            let (r, idx) = obj.id.lookup(map)?;
            let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
            if road.lanes_ltr[idx].lt != obj.orig_lt {
                bail!("{:?} lane type has changed", obj);
            }
            road.lanes_ltr[idx].lt = obj.lt;
        } else if let Some(obj) = cmd.remove("ReverseLane") {
            let obj: ReverseLane = serde_json::from_value(obj).unwrap();
            let (r, idx) = obj.l.lookup(map)?;
            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 {

View on GitHub (pinned to 0964f29315)