atuinsh/atuin · critical
overflowed maximum capacity
Error message
overflowed maximum capacity
What it means
`boxcar::Vec::push` reserves a slot by incrementing an `inflight` u64 counter and converting it to u32 with `.expect("overflowed maximum capacity")`. The conversion fails once 2^32 items are already in flight: the lock-free vector's u32 index space is full. This is a hard capacity ceiling - atuin-nucleo cannot index more than 4,294,967,295 items.
Source
Thrown at crates/atuin-nucleo/src/boxcar.rs:139
// bucket is uninitialized
if entries.is_null() {
return None;
}
// safety: `location.entry` is always in bounds for it's bucket
let entry = Bucket::<T>::get(entries, location.entry, self.columns);
// safety: the entry is active
(*entry).active.load(Ordering::Acquire).then(|| Entry::read(entry, self.columns))
}
}
/// Appends an element to the back of the vector.
pub fn push(&self, value: T, fill_columns: impl FnOnce(&T, &mut [Utf32String])) -> u32 {
let index = self.inflight.fetch_add(1, Ordering::Release);
// the inflight counter is a `u64` to catch overflows of the vector'scapacity
let index: u32 = index.try_into().expect("overflowed maximum capacity");
let location = Location::of(index);
// eagerly allocate the next bucket if we are close to the end of this one
if index == (location.bucket_len - (location.bucket_len >> 3)) {
if let Some(next_bucket) = self.buckets.get(location.bucket as usize + 1) {
Vec::get_or_alloc(next_bucket, location.bucket_len << 1, self.columns);
}
}
// safety: `location.bucket` is always in bounds
let bucket = unsafe { self.buckets.get_unchecked(location.bucket as usize) };
let mut entries = bucket.entries.load(Ordering::Acquire);
// the bucket has not been allocated yet
if entries.is_null() {
entries = Vec::get_or_alloc(bucket, location.bucket_len, self.columns);
}
View on GitHub (pinned to 15fe1318f1)
Solutions
- Bound the number of pushed items to `u32::MAX` by tracking a running count and stopping before the ceiling
- Ensure the dataset is pushed exactly once - no re-injection loops that duplicate entries
- If you genuinely need more rows, shard across multiple matcher instances
Example fix
// before
matcher.push(item, |item, cols| fill(item, cols));
// after
const CEILING: u64 = u32::MAX as u64;
if pushed < CEILING {
matcher.push(item, |item, cols| fill(item, cols));
pushed += 1;
} else {
// report capacity exhaustion instead of panicking
} Defensive patterns
Strategy: validation
Validate before calling
// Bound total pushes to the u32 index space
const CEILING: u64 = u32::MAX as u64;
if injected_count >= CEILING {
return Err(CapacityError);
}
matcher.push(item, fill);
injected_count += 1; Type guard
fn fits_u32_index(count: u64) -> bool {
count < u64::from(u32::MAX)
} Prevention
- Track a cumulative injected-item counter instead of relying on the library's internal overflow panic
- Push each dataset entry once; never re-inject inside per-keystroke loops
- Shard datasets larger than ~4B rows across multiple matcher instances
When it happens
Trigger: Calling `push` (e.g. injecting into a `Nucleo` matcher) after roughly 4.29 billion prior pushes/extends on the same instance, such as a loop that re-injects the whole dataset per keystroke without resetting.
Common situations: Unreachable for atuin's real history workloads; appears in stress tests, fuzzing, or bugs that re-push the entire dataset repeatedly.
Related errors
- exceeded maximum length
- exceeded maximum allocation size
- creating threadpool failed
- payload length fits in u32
- issue in stats average query
AI-assisted analysis of atuinsh/atuin@15fe1318f1 (2026-08-19).
Data as JSON: /api/errors/f8a9b4a628eebca4.
Report an issue: GitHub.