stride3d/stride · error · InvalidOperationException
AVAssetReader.StartReading (audio) failed
Error message
AVAssetReader.StartReading (audio) failed: {(err != null ? err.LocalizedDescription : "unknown")} What it means
StreamedBufferSoundSource (AVFoundation backend) throws this when AVAssetReader.StartReading() fails after the audio output has been attached. The reader cannot begin producing decoded audio frames, so streaming cannot proceed. The AVAssetReader.Error LocalizedDescription is embedded to reveal the underlying cause (format mismatch, corrupted asset, expired track, etc.).
Solutions
- Inspect the embedded LocalizedDescription in the exception message for the concrete AVFoundation reason
- Verify the source file is decodable (playable in AVPlayer / afinfo) before creating the reader
- Check that the reader output settings (outputSettings dict on AVAssetReaderAudioMixOutput) request a supported format like Mono16 or Stereo16 PCM
- Re-create the reader (DisposeReader + re-init) after seeking instead of reusing a reader whose StartReading already failed
- Ensure the asset still has an audio track (check track.Count > 0 and mediaType == AVMediaType.Audio) before adding the output
Example fix
// before
var settings = new AVAudioSettingsFloat { AVFormatIDKey = (int)AVAudioFormat.ThreeSixty }; // unsupported
audioReader.AddOutput(new AVAssetReaderAudioMixOutput(new[] { audioTrack }, settings));
audioReader.StartReading();
// after
var settings = new AVAudioSettingsFloat
{
AVFormatIDKey = (int)CMAudioFormatID.LinearPCM,
AVLinearPCMBitDepthKey = 16,
AVLinearPCMIsFloatKey = false,
AVLinearPCMIsBigEndianKey = false,
AVNumberOfChannelsKey = 1
};
var output = new AVAssetReaderAudioMixOutput(new[] { audioTrack }, settings);
audioReader.AddOutput(output);
if (!audioReader.StartReading())
Logger.Error($"StartReading failed: {audioReader.Error?.LocalizedDescription}"); Defensive patterns
Strategy: try-catch
Validate before calling
// AVFoundation/macOS
if (audioTrack == null || asset.Tracks.Length == 0)
throw new InvalidOperationException("Asset has no audio track");
var playable = asset.IsPlayable;
if (!playable) throw new InvalidOperationException("Asset not playable"); Type guard
bool HasReadableAudioTrack(AVAssetReader reader) => reader != null && reader.Status == AVAssetReaderStatus.Unknown && reader.Error == null;
Try / catch
try
{
source = new StreamedBufferSoundSource(path, services);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("StartReading"))
{
Logger.Error($"Audio reader init failed: {ex.Message}");
source = null; // fall back to non-streamed sound
} Prevention
- Check asset tracks and playability before constructing the reader
- Use standard PCM output settings (16-bit linear PCM) supported by all devices
- Test with known-good media files (afinfo/ffprobe) before shipping
- Re-create readers after seek instead of reusing failed ones
When it happens
Trigger: Calling InitializeMediaExtractor or SeekInternalImpl on macOS/iOS builds with an audio file whose audioTrack was invalidated or whose AVAssetReaderAudioMixOutput settings (AVFormatIDKey/AVNumberOfChannelsKey) are unsupported by the decoder; StartReading returns false and the exception is thrown.
Common situations: Loading an audio file with a sample-rate/channel-count conversion AVFoundation cannot perform; reading a DRM-protected or corrupt media file; seeking on a disposed/re-created reader after the asset track was released; wrong outputSettings keys passed to the reader.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- AVAssetReader (audio) create failed
- Failed to create an AudioLayer Source
- Required extension is not available
- AVFoundationVideoBackend already initialized.
- AVAssetReader create failed
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/0d82ef3fac768a99.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Audio/StreamedBufferSoundSource.AVFoundation.cs:118
audioReader.TimeRange = new CMTimeRange { Start = startCMTime, Duration = CMTime.PositiveInfinity };
}
// 16-bit signed LE interleaved PCM — the format Stride.Audio consumes downstream.
var settings = new NSMutableDictionary
{
[AVAudioSettings.AVFormatIDKey] = NSNumber.FromUInt32((uint)AudioFormatType.LinearPCM),
[AVAudioSettings.AVLinearPCMBitDepthKey] = NSNumber.FromInt32(16),
[AVAudioSettings.AVLinearPCMIsBigEndianKey] = NSNumber.FromBoolean(false),
[AVAudioSettings.AVLinearPCMIsFloatKey] = NSNumber.FromBoolean(false),
[AVAudioSettings.AVLinearPCMIsNonInterleaved] = NSNumber.FromBoolean(false),
};
audioOutput = new AVAssetReaderAudioMixOutput(new[] { audioTrack }, settings);
audioReader.AddOutput(audioOutput);
if (!audioReader.StartReading())
{
var err = audioReader.Error;
throw new InvalidOperationException(
$"AVAssetReader.StartReading (audio) failed: {(err != null ? err.LocalizedDescription : "unknown")}");
}
}
private void DisposeReader()
{
audioOutput?.Dispose();
audioOutput = null;
audioReader?.Dispose();
audioReader = null;
}
private bool ExtractSomeAudioData(out bool endOfFile)
{
endOfFile = audioExtractionDone;
if (audioExtractionDone || audioOutput == null)
return false;
View on GitHub (pinned to 96fad776d2)