a-b-street/abstreet · error

Can't delete ; it's not in the World

Error message

Can't delete {:?}; it's not in the World

What it means

World::delete panics when the given ID is not present in the World's object map. The library tracks drawable objects by ID; deleting something already removed or never added is a caller bug, so it panics rather than silently no-op. Use maybe_delete when a missing object is expected.

Solutions

  1. Switch to world.maybe_delete(id) if absence is a legitimate case.
  2. Guard with a check that the ID is still tracked before deleting.
  3. Deduplicate the call path so delete is invoked once per object lifecycle.

Example fix

// before
world.delete(id);
// after
world.maybe_delete(id);
Defensive patterns

Strategy: fallback

Validate before calling

// Only delete IDs you currently own/track
if tracked_ids.contains(&id) { world.delete(id); }

Try / catch

world.maybe_delete(id); // tolerant variant instead of delete

Prevention

When it happens

Trigger: Calling world.delete(id) twice for the same ID, deleting an ID created by another World instance, or deleting after the object was already consumed/removed elsewhere.

Common situations: Double-handling a UI callback (click handler fires delete twice), stale IDs captured from a previous draw cycle, objects removed by map_editor logic before a subsequent delete call.

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/6df16277e7177ae4. Report an issue: GitHub.

Appendix: source

Thrown at widgetry/src/mapspace/world.rs:412

            if self.dragging_from.is_some() {
                panic!("Can't delete {:?} mid-drag", id);
            }
        }

        self.delete_before_replacement(id);
    }

    /// Delete an object, with the promise to recreate it with the same ID before the next call to
    /// `event`. This may be called while the object is being hovered on or dragged.
    pub fn delete_before_replacement(&mut self, id: ID) {
        if self.objects.remove(&id).is_some() {
            if self.quadtree.remove(id).is_none() {
                // This can happen for objects that're out-of-bounds. One example is intersections
                // in map_editor.
                warn!("{:?} wasn't in the quadtree", id);
            }
        } else {
            panic!("Can't delete {:?}; it's not in the World", id);
        }
    }

    /// Like delete, but doesn't crash if the object doesn't exist
    pub fn maybe_delete(&mut self, id: ID) {
        if self.hovering == Some(id) {
            self.hovering = None;
            self.draw_hovering = None;
            if self.dragging_from.is_some() {
                panic!("Can't delete {:?} mid-drag", id);
            }
        }

        if self.objects.remove(&id).is_some() {
            if self.quadtree.remove(id).is_none() {
                // This can happen for objects that're out-of-bounds. One example is intersections
                // in map_editor.
                warn!("{:?} wasn't in the quadtree", id);

View on GitHub (pinned to 0964f29315)