{"record":{"id":"de695d9905d4bc0a","repo":"a-b-street/abstreet","slug":"lane-type-has-changed","errorCode":null,"errorMessage":"{:?} lane type has changed","messagePattern":"(.+?) lane type has changed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/edits/compat.rs","lineNumber":225,"sourceCode":"// target a single lane in favor of a consolidated ChangeRoad.\nfn fix_old_lane_cmds(value: &mut Value, map: &Map) -> Result<()> {\n    // TODO Can we assume map is in its original state? I don't think so... it may have edits\n    // applied, right?\n\n    let mut modified: BTreeMap<RoadID, EditRoad> = BTreeMap::new();\n    let mut commands = Vec::new();\n    for mut orig in value.as_object_mut().unwrap()[\"commands\"]\n        .as_array_mut()\n        .unwrap()\n        .drain(..)\n    {\n        let cmd = orig.as_object_mut().unwrap();\n        if let Some(obj) = cmd.remove(\"ChangeLaneType\") {\n            let obj: ChangeLaneType = serde_json::from_value(obj).unwrap();\n            let (r, idx) = obj.id.lookup(map)?;\n            let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));\n            if road.lanes_ltr[idx].lt != obj.orig_lt {\n                bail!(\"{:?} lane type has changed\", obj);\n            }\n            road.lanes_ltr[idx].lt = obj.lt;\n        } else if let Some(obj) = cmd.remove(\"ReverseLane\") {\n            let obj: ReverseLane = serde_json::from_value(obj).unwrap();\n            let (r, idx) = obj.l.lookup(map)?;\n            let dst_i = map.find_i_by_osm_id(obj.dst_i)?;\n            let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));\n            let edits_dir = if dst_i == map.get_r(r).dst_i {\n                Direction::Fwd\n            } else if dst_i == map.get_r(r).src_i {\n                Direction::Back\n            } else {\n                bail!(\"{:?}'s road doesn't point to dst_i at all\", obj);\n            };\n            if road.lanes_ltr[idx].dir == edits_dir {\n                bail!(\"{:?}'s road already points to dst_i\", obj);\n            }\n            road.lanes_ltr[idx].dir = edits_dir;","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/edits/compat.rs#L207-L243","documentation":"While upgrading old edit files, fix_old_lane_cmds replays a ChangeLaneType command only if the lane still has the same original type recorded in the command (obj.orig_lt). If the road's lane type has since changed (map regeneration, another edit, OSM update), the edit's precondition no longer holds, so upgrade aborts with this error instead of silently corrupting the lane.","triggerScenarios":"Calling Map Edits load (compat::upgrade) on a saved edit JSON containing ChangeLaneType whose `old`/orig_lt no longer equals road.lanes_ltr[idx].lt in the current map — e.g. the basemap was re-generated or the lane was edited before.","commonSituations":"Loading old saved edits after re-importing the map from OSM; applying edits made before another edit changed the lane type; sharing edit files between differently-versioned map builds.","solutions":["Delete the stale ChangeLaneType command from the edit JSON (or recreate the edit against the current map).","Regenerate the map so lane types match what the edit expects, then load the edit.","If the new type is acceptable, hand-edit the edit file so orig_lt matches the current lane type."],"exampleFix":"// before: loading stale edit\nlet edits = Edits::load_from_file(map, path);\n// after: strip the broken command first\nlet mut v: serde_json::Value = serde_json::from_reader(file)?;\nfor cmd in v[\"commands\"].as_array_mut().unwrap() {\n    cmd.as_object_mut().unwrap().remove(\"ChangeLaneType\");\n}\nlet edits = Edits::load_from_bytes(map, &serde_json::to_vec(&v)?);","handlingStrategy":"try-catch","validationCode":"// check the precondition before loading\nlet (r, idx) = cmd.id.lookup(map)?;\nif map.get_r_edit(r).lanes_ltr[idx].lt != cmd.orig_lt { /* drop or repair cmd */ }","typeGuard":null,"tryCatchPattern":"match Edits::load_from_file(map, path) {\n    Ok(e) => apply(e),\n    Err(err) if err.to_string().contains(\"lane type has changed\") => {\n        warn!(\"stale ChangeLaneType dropped: {}\", err);\n        // re-create edit without that command\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Regenerate edits whenever the basemap is re-imported from OSM.","Apply edits promptly after creating them; don't hoard old edit files across map versions.","Keep a record of which map name/version each edit file targets."],"tags":["edits","compatibility","state-mismatch"],"backgroundTag":"schema-validation-failed","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"}