bevyengine/bevy · critical

array layout should be valid

Error message

array layout should be valid

What it means

BlobArray::alloc computes the allocation layout as item_layout.repeat_packed(capacity) and expects it to be valid. Layout::repeat_packed only fails when capacity * item_size overflows the address space (result must stay within usize/isize bounds), so this panic means the requested element count times the item size is unrepresentable - a corrupted or astronomically large capacity, not a normal out-of-memory.

Source

Thrown at crates/bevy_ecs/src/storage/blob_array.rs:264

            let item = self.get_unchecked_mut(last_element_index).promote();
            // SAFETY:
            unsafe { drop(item) };
            self.drop = Some(drop);
        }
    }

    /// Allocate a block of memory for the array. This should be used to initialize the array, do not use this
    /// method if there are already elements stored in the array - use [`Self::realloc`] instead.
    ///
    /// # Panics
    /// - Panics if the new capacity overflows `isize::MAX` bytes.
    /// - Panics if the allocation causes an out-of-memory error.
    pub(super) fn alloc(&mut self, capacity: NonZeroUsize) {
        #[cfg(debug_assertions)]
        debug_assert_eq!(self.capacity, 0);
        if !self.is_zst() {
            let new_layout = self.item_layout.repeat_packed(capacity.get());
            let new_layout = new_layout.expect("array layout should be valid");
            // SAFETY: layout has non-zero size because capacity > 0, and the blob isn't ZST (`self.is_zst` == false)
            let new_data = unsafe { alloc::alloc::alloc(new_layout) };
            self.data = NonNull::new(new_data).unwrap_or_else(|| handle_alloc_error(new_layout));
        }
        #[cfg(debug_assertions)]
        {
            self.capacity = capacity.into();
        }
    }

    /// Reallocate memory for this array.
    /// For example, if the length (number of stored elements) reached the capacity (number of elements the current allocation can store),
    /// you might want to use this method to increase the allocation, so more data can be stored in the array.
    ///
    /// # Panics
    /// - Panics if the new capacity overflows `isize::MAX` bytes.
    /// - Panics if the allocation causes an out-of-memory error.
    ///

View on GitHub (pinned to 396ca72708)

Solutions

  1. Trace where the capacity value comes from and fix the size computation - the panic means bytes = capacity x item_size overflowed.
  2. Validate/sanitize any size read from files/network (bounds caps, checked_mul) before it drives storage sizing.
  3. If the allocation is legitimately huge, batch it into smaller chunks or shrink the component layout.
Defensive patterns

Strategy: validation

Validate before calling

fn capacity_is_representable(item_layout: Layout, capacity: usize) -> bool {
    item_layout.repeat_packed(capacity).is_ok()
}

Prevention

When it happens

Trigger: A capacity value computed from untrusted or serialized input (entity counts, table sizing) that overflows before reaching alloc; arithmetic bugs producing near-usize::MAX element counts; huge component sizes multiplied by large capacities.

Common situations: Deserializing untrusted length prefixes; capacity math using wrapping/underflowed intermediates; 32-bit targets where the address space is exhausted by modest counts of large components.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/b338102b7f8f133a. Report an issue: GitHub.