Zackriya-Solutions/meetily · info

Buffer should always be available

Error message

Buffer should always be available

What it means

PooledBuffer.as_mut() expects the inner Option<Vec<f32>> to be Some. It is Some from construction and is only taken by into_inner (which consumes self) and by Drop, so a None here requires using the wrapper while Drop is running (reentrant drop) or unsafe/mem::forget misuse. In correct code this invariant cannot be violated.

Source

Thrown at frontend/src-tauri/src/audio/buffer_pool.rs:91

/// RAII wrapper that automatically returns buffer to pool when dropped
pub struct PooledBuffer {
    buffer: Option<Vec<f32>>,
    pool: AudioBufferPool,
}

impl PooledBuffer {
    /// Create a new pooled buffer
    pub fn new(pool: AudioBufferPool) -> Self {
        let buffer = pool.get_buffer();
        Self {
            buffer: Some(buffer),
            pool,
        }
    }

    /// Get mutable access to the underlying buffer
    pub fn as_mut(&mut self) -> &mut Vec<f32> {
        self.buffer.as_mut().expect("Buffer should always be available")
    }

    /// Get immutable access to the underlying buffer
    pub fn as_ref(&self) -> &Vec<f32> {
        self.buffer.as_ref().expect("Buffer should always be available")
    }

    /// Consume the wrapper and return the buffer (will not be returned to pool)
    pub fn into_inner(mut self) -> Vec<f32> {
        self.buffer.take().expect("Buffer should always be available")
    }
}

impl Drop for PooledBuffer {
    fn drop(&mut self) {
        if let Some(buffer) = self.buffer.take() {
            self.pool.return_buffer(buffer);
        }

View on GitHub (pinned to 0281737d87)

Solutions

  1. Treat the panic as a design smell: audit for any buffer.take() outside into_inner and Drop
  2. If it fires, run with RUST_BACKTRACE=1 to find the reentrant Drop or aliasing site
  3. Consider returning Option/&mut from accessors or making take-sites explicit so misuse becomes a compile error
Defensive patterns

Strategy: try-catch

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    pooled.as_mut().extend_from_slice(chunk);
}));
if result.is_err() {
    log::error!("audio worker panicked; replacing pooled buffer");
    *pooled = PooledBuffer::new(pool.clone());
}

Prevention

When it happens

Trigger: A Drop implementation on a struct containing PooledBuffer that calls as_mut on it during teardown, refactoring that adds an early buffer.take(), or double-use after into_inner via unsafe aliasing. Normal new -> as_mut -> drop usage never triggers it.

Common situations: Almost never observed; appears after refactors that move take() outside into_inner/Drop or when wrapping PooledBuffer in types with custom Drop handlers in the audio pipeline.

Related errors


AI-assisted analysis of Zackriya-Solutions/meetily@0281737d87 (2026-08-16). Data as JSON: /api/errors/6ac51c5d1b49e87d. Report an issue: GitHub.