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

  1. Ensure every node is inserted into the NodeMap (via insert_or_ignore) before calling get
  2. Use entry or a get_or_insert-style API so the node is added on first reference
  3. Use the non-panicking lookup (node_to_id.get) and handle the None case yourself
  4. 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

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


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)