atuinsh/atuin · critical

exceeded maximum allocation size

Error message

exceeded maximum allocation size

What it means

`Bucket::layout` builds the allocation layout for a bucket's entry array with `Layout::from_size_align(...).expect("exceeded maximum allocation size")`. It fails when `entry_size * bucket_len` exceeds `isize::MAX`, the largest allocation Rust permits. Bucket lengths double per bucket up to 2^31 entries, so this is reachable only near the tail of a multi-billion-entry vector, or with an oversized element type `T`.

Source

Thrown at crates/atuin-nucleo/src/boxcar.rs:495

                vec: self.vec,
            },
            ParIterProducer {
                start: self.start + index,
                end: self.end,
                vec: self.vec,
            },
        )
    }
}

struct Bucket<T> {
    entries: AtomicPtr<Entry<T>>,
}

impl<T> Bucket<T> {
    fn layout(len: u32, layout: Layout) -> Layout {
        Layout::from_size_align(layout.size() * len as usize, layout.align())
            .expect("exceeded maximum allocation size")
    }

    unsafe fn alloc(len: u32, cols: u32) -> *mut Entry<T> {
        let layout = Entry::<T>::layout(cols);
        let arr_layout = Self::layout(len, layout);
        let entries = std::alloc::alloc(arr_layout);
        if entries.is_null() {
            std::alloc::handle_alloc_error(arr_layout)
        }

        for i in 0..len {
            let active = entries.add(i as usize * layout.size()) as *mut AtomicBool;
            active.write(AtomicBool::new(false))
        }
        entries as *mut Entry<T>
    }

    unsafe fn dealloc(entries: *mut Entry<T>, len: u32, cols: u32) {

View on GitHub (pinned to 15fe1318f1)

Solutions

  1. Keep the total item count far below the u32 boundary so late buckets are never allocated
  2. Pre-check intended sizes before pushing: `size_of::<T>() * planned_len <= isize::MAX as usize`
  3. Report upstream if hit with a plausible workload - capacity math should degrade gracefully, not abort

Example fix

// before
vec.push(item, fill); // let bucket allocation fail late

// after
let planned = existing + 1;
assert!(std::mem::size_of::<T>() as u64 * planned as u64 <= isize::MAX as u64);
vec.push(item, fill);
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check allocation math for your element type and scale
let per_entry = std::mem::size_of::<T>() as u64;
if per_entry.saturating_mul(planned_total) > isize::MAX as u64 {
    return Err(AllocationTooLarge);
}

Prevention

When it happens

Trigger: Allocating the last buckets of a boxcar vector already holding ~4 billion items; a custom `T` so large that even moderate bucket lengths overflow `isize::MAX`.

Common situations: The same practically-unreachable territory as the u32 capacity panics: stress tests, fuzzing, or accidental re-push loops that inflate the vector to billions of rows.

Related errors


AI-assisted analysis of atuinsh/atuin@15fe1318f1 (2026-08-19). Data as JSON: /api/errors/7e3eb47830f6d608. Report an issue: GitHub.