FyroxEngine/Fyrox · critical

Zero duration buffer

Error message

Zero duration buffer: {:?}

What it means

A sound source requires a buffer with non-zero playback length; a zero-duration buffer cannot be played or scheduled. GenericSource::set_buffer checks locked_buffer.duration() and panics with the buffer's debug representation when it is Duration::ZERO (note: a failed load returns SoundError::BufferFailedToLoad instead).

Solutions

  1. Replace the audio file with a real, non-empty asset; verify duration > 0 before use.
  2. Check the buffer before assigning: if buffer.duration() == Duration::ZERO, skip/replace it instead of calling set_buffer.
  3. Regenerate or re-export the sound asset and confirm sample count at import time.
  4. If buffers are built procedurally, return SoundError (or skip) when sample data is empty rather than constructing a zero-length buffer.

Example fix

// before
source.set_buffer(Some(empty_buffer)); // panics: duration == 0
// after
if buffer.duration() > Duration::ZERO {
    source.set_buffer(Some(buffer));
} else {
    eprintln!("skipping empty audio buffer");
}
Defensive patterns

Strategy: validation

Validate before calling

fn playable(buffer: &SoundBuffer) -> bool {
    buffer.duration() > Duration::ZERO
}
// only call set_buffer(Some(b)) when playable(&b)

Prevention

When it happens

Trigger: Calling set_buffer(Some(buffer)) on a sound source (directly or via SoundBuilder::build) with a SoundBuffer whose decoded duration is zero — e.g. a 0-byte or empty audio file loaded successfully into an empty buffer.

Common situations: Empty/truncated audio files (0-sample wav/ogg) shipped with the game; a generator that produced no samples; file corruption or wrong loader producing empty frames; placeholder audio files never filled in.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of FyroxEngine/Fyrox@76c91aad8e (2026-09-10). Data as JSON: /api/errors/7435c5dce31ec754. Report an issue: GitHub.

Appendix: source

Thrown at fyrox-sound/src/source.rs:229

        buffer: Option<SoundBufferResource>,
    ) -> Result<Option<SoundBufferResource>, SoundError> {
        self.buf_read_pos = 0.0;
        self.playback_pos = 0.0;

        // If we already have streaming buffer assigned make sure to decrease use count
        // so it can be reused later on if needed.
        if let Some(buffer) = self.buffer.clone() {
            if let Some(SoundBuffer::Streaming(streaming)) = buffer.state().data() {
                streaming.use_count = streaming.use_count.saturating_sub(1);
            }
        }

        if let Some(buffer) = buffer.clone() {
            match buffer.state().data() {
                None => return Err(SoundError::BufferFailedToLoad),
                Some(locked_buffer) => {
                    if locked_buffer.duration() == Duration::ZERO {
                        panic!("Zero duration buffer: {:?}", locked_buffer);
                    }
                    // Check new buffer if streaming - it must not be used by anyone else.
                    if let SoundBuffer::Streaming(ref mut streaming) = *locked_buffer {
                        if streaming.use_count != 0 {
                            return Err(SoundError::StreamingBufferAlreadyInUse);
                        }
                        streaming.use_count += 1;
                    }
                }
            }
        }

        Ok(std::mem::replace(&mut self.buffer, buffer))
    }

    /// Returns current buffer if any.
    pub fn buffer(&self) -> Option<SoundBufferResource> {
        self.buffer.clone()

View on GitHub (pinned to 76c91aad8e)