stride3d/stride · error · InvalidOperationException
AVAssetReader (audio) create failed
Error message
AVAssetReader (audio) create failed: {error.LocalizedDescription} What it means
On iOS/macOS, StreamedBufferSoundSource.CreateAudioReader builds an AVAssetReader for the audio track of an asset. If AVAssetReader.FromAsset reports a non-null NSError, the reader could not be created and the method throws with the OS's localized description. Without a reader the streamed audio cannot be decoded.
Solutions
- Read error.LocalizedDescription from the exception to identify the OS-level cause (missing file vs. undecodable format).
- Confirm the media file is deployed as a BundleResource and reachable via the URL passed to the sound loader.
- Re-encode the audio to a broadly supported format (AAC/M4A or CBR MP3) if the codec is unsupported or DRM-protected.
- Re-copy or re-download the file if it was removed or corrupted, then retry playback.
Example fix
// before
var sound = Sound.LoadStreamed(services, "music/track.wma"); // AVAssetReader fails on unsupported codec
// after
// ship music/track.m4a (AAC) as BundleResource instead
var sound = Sound.LoadStreamed(services, "music/track.m4a");
try { sound.CreateInstance(listener); }
catch (InvalidOperationException ex) { Log.Error($"Audio open failed: {ex.Message}"); } Defensive patterns
Strategy: try-catch
Validate before calling
// before loading, on iOS/macOS
var url = NSBundle.MainBundle.GetUrlForResource("track", "m4a", "music");
if (url == null || !NSFileManager.DefaultManager.FileExists(url.Path))
Log.Error("Audio asset missing from app bundle"); Type guard
bool IsPlayableMediaAsset(NSUrl assetUrl) =>
assetUrl != null && NSFileManager.DefaultManager.FileExists(assetUrl.Path);
Try / catch
try
{
var sound = Sound.LoadStreamed(services, mediaUrl);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("AVAssetReader"))
{
Log.Error($"Media cannot be decoded on this device: {ex.Message}");
// fallback: use a pre-encoded AAC copy or skip audio
} Prevention
- Deploy media as BundleResource and verify via NSBundle URL lookup before playback.
- Ship audio in AVFoundation-friendly formats (AAC/M4A, CBR MP3).
- Avoid DRM-protected or exotic-codec files for streamed playback.
- Validate user-picked files by probing them with AVAsset immediately after selection, before storing the URL.
When it happens
Trigger: Initializing the media extractor or seeking (SeekInternalImpl) on an AVFoundation-backed streamed sound when AVAssetReader creation fails — asset URL points to a missing/unreadable file, the asset has no compatible audio track, or the file format is not decodable by AVFoundation.
Common situations: Media file not copied into the app bundle (missing BundleResource build action) so the asset URL is invalid, DRM-protected or unsupported codec/container (e.g. some WMA/FLAC variants), file deleted from the sandbox after being picked, corrupted download.
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.StartReading (audio) failed
- Unable to read
- 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/972cea3d8236ec30.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Audio/StreamedBufferSoundSource.AVFoundation.cs:95
audioAsset = null;
audioTrack = null;
if (audioTempFilePath != null && File.Exists(audioTempFilePath))
{
try { File.Delete(audioTempFilePath); } catch { /* best-effort */ }
}
audioTempFilePath = null;
}
private void CreateAudioReader(TimeSpan startTime)
{
DisposeReader();
if (audioAsset == null || audioTrack == null)
return;
audioReader = AVAssetReader.FromAsset(audioAsset, out var error);
if (error != null)
throw new InvalidOperationException($"AVAssetReader (audio) create failed: {error.LocalizedDescription}");
if (startTime > TimeSpan.Zero)
{
var startCMTime = new CMTime((long)(startTime.TotalMilliseconds), 1000);
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);View on GitHub (pinned to 96fad776d2)