Quick answer: ma_device_init can fail when there’s no output device (CI machines, fresh VMs, unplugged DAC). Handle the failure, run audio-less, and re-init on device change.

A game crashes on startup in CI and on some user machines — ma_device_init returns an error and the code dereferences an uninitialized device.

Always Check the Result

ma_result r = ma_device_init(NULL, &config, &device);
if (r != MA_SUCCESS) {
    log_warn("Audio device init failed: %d — running muted", r);
    g_audioAvailable = false;
    return;   // don't touch `device`
}
g_audioAvailable = true;

A headless CI box or a VM with no sound card legitimately has no default device. Don’t treat that as fatal.

Run Audio-Less Gracefully

Gate every play call behind g_audioAvailable. The game runs silently — correct behavior for automated tests and headless servers.

Handle Device Changes

A device can vanish at runtime (USB headset unplugged). Register a notification callback (ma_device_notification_proc) and re-init when the default device changes — otherwise audio dies silently after a hot-unplug.

Pick a Backend Explicitly If Needed

If a specific backend (WASAPI, CoreAudio, ALSA) is unreliable in your environment, set the backend priority list in the context config rather than letting miniaudio auto-pick.

Verifying

The game launches on a headless CI machine without crashing (muted). On a desktop, audio works; unplugging a device and replugging recovers playback.

“No audio device is a valid state. Check the result, run muted, re-init on device change.”

‘Audio is optional’ should be a design assumption — it makes CI, servers, and accessibility all easier.