Zackriya-Solutions/meetily · error

No audio samples decoded from file

Error message

No audio samples decoded from file

What it means

Raised by decode_audio_file_with_progress (decoder.rs:558) after the Symphonia packet loop finished with an empty all_samples vector. The container probed successfully and an audio track was found (earlier guards for probe/no-track/sample-rate already passed), but every packet either failed to decode (each decode error is only warned and skipped via `continue`), belonged to a different track_id, or the stream hit EOF before yielding any audio. In practice this means the file is structurally an audio file but contains zero decodable audio data.

Source

Thrown at frontend/src-tauri/src/audio/decoder.rs:558

                        last_progress = current_progress;
                        callback(current_progress, &format!("Decoding audio: {}%", current_progress));
                    }
                }
            }
            Err(e) => {
                warn!("Error decoding packet: {}", e);
                continue;
            }
        }
    }

    // Ensure we report 100% completion
    if let Some(callback) = &progress_callback {
        callback(100, "Decoding complete");
    }

    if all_samples.is_empty() {
        return Err(anyhow!("No audio samples decoded from file"));
    }

    let total_frames = all_samples.len() / channels as usize;
    let duration_seconds = total_frames as f64 / sample_rate as f64;

    info!(
        "Decoded {} samples ({:.2}s) at {}Hz, {} channels",
        all_samples.len(),
        duration_seconds,
        sample_rate,
        channels
    );

    Ok(DecodedAudio {
        samples: all_samples,
        sample_rate,
        channels,
        duration_seconds,

View on GitHub (pinned to 0281737d87)

Solutions

  1. Inspect the preceding warn! log lines ('Error reading packet'/'Error decoding packet') - they name the Symphonia error that caused every packet to be skipped; that is the root cause.
  2. Test the file outside the app with ffprobe (or ffmpeg -v error -i file -f null -) to confirm it has a decodable audio stream and non-zero duration.
  3. If ffprobe shows a codec Symphonia lacks (e.g. Opus in MP4, ATRAC, WMA Pro), re-encode once: ffmpeg -i in.ext -ac 1 -ar 16000 out.wav and import the WAV.
  4. If ffmpeg is not installed, install it and retry so the MKV/WebM/WMA pre-conversion path in decoder.rs runs instead of Symphonia failing packet-by-packet.
  5. For a file still being written (live recording/export), wait until the writer finalizes the container (moov atom for MP4) and retry.
  6. If the first track is not audio, extend track selection at decoder.rs:448 to prefer tracks whose codec_params indicate real audio (declared sample_rate) instead of the first non-NULL codec.

Example fix

// before: first non-null codec track wins, may pick a data/cover track
let track = format.tracks().iter().find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
    .ok_or_else(|| anyhow!("No audio track found in file"))?;

// after: prefer a track with declared sample_rate (real audio)
let track = format.tracks().iter()
    .filter(|t| t.codec_params.codec != CODEC_TYPE_NULL)
    .max_by_key(|t| t.codec_params.sample_rate.is_some() as u8)
    .ok_or_else(|| anyhow!("No audio track found in file"))?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust caller: cheap pre-check that the file has a decodable audio track
fn has_decodable_audio(path: &Path) -> bool {
    let Ok(f) = std::fs::File::open(path) else { return false };
    let mss = symphonia::default::MediaSourceStream::new(Box::new(f), Default::default());
    let Ok(probed) = symphonia::default::get_probe().format(&Hint::new(), mss, &Default::default(), &Default::default()) else { return false };
    probed.format.tracks().iter().any(|t| t.codec_params.sample_rate.is_some())
}
// or shell out: ffmpeg -v error -i file -f null - ; exit 0 means decodable

Try / catch

// Treat decode failure as: try ffmpeg fallback once, else surface a clear message
let decoded = match decode_audio_file(&path) {
    Ok(d) => d,
    Err(e) if e.to_string().contains("No audio samples decoded") => {
        let wav = convert_to_wav_16k_via_ffmpeg(&path)?; // explicit fallback
        decode_audio_file(&wav)?
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling decode_audio_file/decode_audio_file_with_progress on a file where: (a) the codec is recognized but every packet decode returns Err (corrupt payload, truncated download, DRM-protected M4A), (b) the selected first audio track is not the track that actually carries packets (packet.track_id() != track_id for all packets), (c) a 0-byte or header-only audio file passed extension validation, or (d) an ffmpeg pre-conversion (MKV/WebM/WMA path) produced a WAV from a video-only source. Note needs_ffmpeg_conversion only fires for a fixed extension list, so e.g. an .mp4 with an unsupported internal codec goes straight to Symphonia.

Common situations: Importing a meeting recording that was still being written when copied; importing DRM-protected iTunes/Audible audio; mislabeled extensions (e.g. an .mp3 that is actually a renamed PDF); WebM/MKV imports without ffmpeg on PATH so the conversion path never runs; files where the first track is a cover-art/video track and the audio track is second.

Related errors


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