a-b-street/abstreet · error
not in NodeMap
Error message
{:?} not in NodeMap What it means
NodeMap::get looks up the NodeId for a node of generic type T; if the node was never inserted into the map, it panics with "{:?} not in NodeMap". The library treats a missing node as a programmer error rather than a recoverable condition, since callers are expected to have inserted every node they later query.
Solutions
- Ensure every node is inserted into the NodeMap (via insert_or_ignore) before calling get
- Use entry or a get_or_insert-style API so the node is added on first reference
- Use the non-panicking lookup (node_to_id.get) and handle the None case yourself
- Verify you are querying the same NodeMap instance the node was registered in
Example fix
// before
let id = node_map.get(node);
// after
let id = match node_map.node_to_id.get(&node) {
Some(id) => *id,
None => { node_map.insert_or_ignore(node.clone()); node_map.get(node) }
}; Defensive patterns
Strategy: validation
Validate before calling
if node_map.node_to_id.contains_key(&node) { let id = node_map.get(node); } else { /* insert or handle missing */ } Type guard
fn lookup<T: Clone + std::hash::Hash + Eq>(map: &NodeMap<T>, node: &T) -> Option<NodeId> {
map.node_to_id.get(node).copied()
} Try / catch
// panics are not catchable in Rust; guard before calling:
let id = node_map.node_to_id.get(&node).copied()
.unwrap_or_else(|| panic_once_with_context(node)); Prevention
- Insert every node before querying; never assume nodes exist
- Prefer insert_or_ignore or entry-based insertion at first reference
- Ensure only one NodeMap instance is used per graph build
- Log/collect missing nodes instead of failing on the first one
When it happens
Trigger: Calling get(node) with a node that was never added to the NodeMap, or calling get on a node from a different NodeMap instance; also via translate_id misuse indirectly after stale indexing.
Common situations: Building a graph where some nodes were skipped during insertion but later referenced during edge/path construction; reusing node keys across maps; typos or case differences in node identifiers.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- This build of A/B Street stores player data in…
- Can't find the data/ directory
- CityName::new( , ) has a country code that isn't two letters
- Couldn't read_json( )
- Couldn't read_binary
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/0b6c783a39e462f0.
Report an issue: GitHub.
Appendix: source
Thrown at map_model/src/pathfind/node_map.rs:42
id_to_node: Vec::new(),
}
}
pub fn get_or_insert(&mut self, node: T) -> NodeId {
if let Some(id) = self.node_to_id.get(&node) {
return *id;
}
let id = self.id_to_node.len();
self.node_to_id.insert(node, id);
self.id_to_node.push(node);
id
}
pub fn get(&self, node: T) -> NodeId {
if let Some(id) = self.node_to_id.get(&node) {
*id
} else {
panic!("{:?} not in NodeMap", node);
}
}
pub fn translate_id(&self, id: usize) -> T {
self.id_to_node[id]
}
/// Call this after filling out the input graph, right before preparation.
pub fn guarantee_node_ordering(&self, input_graph: &mut InputGraph) {
// The fast_paths implementation will trim out the last nodes in the input graph if there
// are no edges involving them:
// https://github.com/easbar/fast_paths/blob/fdb65f25c5485c9c74c1b3cbe66d829eea81b14b/src/input_graph.rs#L151
//
// We sometimes add nodes that aren't used yet, so that we can reuse the same node ordering
// later. Detect if the last node isn't used.
let last_node = self.id_to_node.len() - 1;
input_graph.freeze();
for edge in input_graph.get_edges() {View on GitHub (pinned to 0964f29315)