{"record":{"id":"77b38285e6c9624e","repo":"a-b-street/abstreet","slug":"expected-turn-but-found","errorCode":null,"errorMessage":"expected turn, but found {:?}","messagePattern":"expected turn, but found (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"map_model/src/pathfind/v1.rs","lineNumber":336,"sourceCode":"            .map(|ut| ut.path.contains(&t))\n            .unwrap_or(false)\n    }\n\n    /// Trusting the caller to do this in valid ways.\n    pub fn modify_step(&mut self, idx: usize, step: PathStep, map: &Map) {\n        assert!(self.currently_inside_ut.is_none());\n        // We're assuming this step was in the middle of the path, meaning we were planning to\n        // travel its full length\n        self.total_length -= self.steps[idx].as_traversable().get_polyline(map).length();\n\n        // When replacing a turn, also update any references to it in uber_turns\n        if let PathStep::Turn(old_turn) = self.steps[idx] {\n            for uts in &mut self.uber_turns {\n                if let Some(turn_idx) = uts.path.iter().position(|i| i == &old_turn) {\n                    if let PathStep::Turn(new_turn) = step {\n                        uts.path[turn_idx] = new_turn;\n                    } else {\n                        panic!(\"expected turn, but found {:?}\", step);\n                    }\n                }\n            }\n        }\n\n        self.steps[idx] = step;\n        self.total_length += self.steps[idx].as_traversable().get_polyline(map).length();\n\n        if self.total_length < Distance::ZERO {\n            panic!(\n                \"modify_step broke total_length, it's now {}\",\n                self.total_length\n            );\n        }\n    }\n\n    pub fn current_step(&self) -> PathStep {\n        self.steps[0]","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/pathfind/v1.rs#L318-L354","documentation":"Path::modify_step replaces a step in the path; when the replaced step is part of an uber_turn, the corresponding uber_turn path entry must also be replaced, which only works if the new step is also a Turn. If the replacement step is not a Turn, the library panics because an uber_turn would be corrupted by inserting a non-turn step into it.","triggerScenarios":"Calling modify_step at an index whose current step is a PathStep::Turn that belongs to an uber_turn, while passing a replacement step that is not PathStep::Turn (e.g. a Lane or Contraflow step).","commonSituations":"Rerouting/editing paths for changed road geometry where a turn inside an uber_turn (complex intersection movement) is replaced with an arbitrary step; tooling that rewrites path steps without checking uber_turn membership.","solutions":["Only pass PathStep::Turn as the replacement when modifying a step inside an uber_turn","Check whether the old step is part of an uber_turn and rebuild the whole path instead of patching the step","Extend modify_step to remove/split the uber_turn when replacing with a non-turn step (library change)","Verify the replacement step's type against PathStep::Turn before calling modify_step"],"exampleFix":"// before\npath.modify_step(idx, PathStep::Contraflow(drive), map);\n// after\nif matches!(path.steps[idx], PathStep::Turn(_)) && !path.uber_turns.iter().any(|ut| ut.path.contains(&old_turn)) {\n    path.modify_step(idx, PathStep::Contraflow(drive), map);\n} else {\n    // rebuild the path; replacing an uber_turn member requires a Turn\n}","handlingStrategy":"type-guard","validationCode":"if let PathStep::Turn(old_turn) = path.steps[idx] {\n    let in_uber_turn = path.uber_turns.iter().any(|ut| ut.path.contains(&old_turn));\n    if in_uber_turn && !matches!(new_step, PathStep::Turn(_)) { /* reject or rebuild path */ }\n}","typeGuard":"fn can_replace(old: &PathStep, new: &PathStep) -> bool {\n    !matches!(old, PathStep::Turn(_)) || matches!(new, PathStep::Turn(_))\n}","tryCatchPattern":"// panics are not catchable; guard the replacement type before calling:\nif can_replace(&path.steps[idx], &new_step) {\n    path.modify_step(idx, new_step, map);\n} else {\n    rebuild_path();\n}","preventionTips":["Check uber_turn membership of the old step before replacing","Only substitute Turn-for-Turn inside uber_turns","Prefer full path rebuilds over step patching near complex intersections","Add a unit test replacing each PathStep variant"],"tags":["rust","panic","type-mismatch","pathfinding"],"backgroundTag":"type-mismatch","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"}