Kodio uses a sealed class hierarchy for errors, enabling exhaustive when expressions and pattern matching. This ensures you handle all possible error cases at compile time.
Error types
All errors extend AudioError:
PermissionDenied
User denied microphone access. Prompt them to enable it in settings.
DeviceNotFound
No microphone or speaker available. Common on simulators or headless systems.
FormatNotSupported
The requested audio format isn't supported on this platform.
DeviceError
Hardware-level failure during recording or playback.
NotInitialized
Android only: Kodio.initialize(context) wasn't called before recording.
AlreadyRecording
Attempted to start recording when a recording is already in progress.
AlreadyPlaying
Attempted to start playback when audio is already playing.
NoRecordingData
Called getRecording() before any audio was recorded.
Unknown
Unexpected error. Check the cause property for details.
Handling errors
Use try-catch with pattern matching:
try {
val recording = Kodio.record(duration = 5.seconds)
} catch (e: AudioError) {
when (e) {
is AudioError.PermissionDenied -> {
// Request permission
Kodio.microphonePermission.request()
}
is AudioError.DeviceNotFound -> {
showError("No microphone found")
}
is AudioError.NotInitialized -> {
// Android: forgot to initialize
showError("Call Kodio.initialize() first")
}
is AudioError.AlreadyRecording -> {
// Ignore or stop current recording
}
else -> {
showError(e.message ?: "Recording failed")
}
}
}
In Compose
RecorderState and PlayerState expose errors via the error property:
val recorderState = rememberRecorderState()
// Show error dialog
recorderState.error?.let { error ->
AlertDialog(
onDismissRequest = { recorderState.clearError() },
title = { Text("Recording Error") },
text = {
Text(when (error) {
is AudioError.PermissionDenied ->
"Microphone access is required. Please enable it in settings."
is AudioError.DeviceNotFound ->
"No microphone detected."
else ->
error.message ?: "An error occurred"
})
},
confirmButton = {
TextButton(onClick = { recorderState.clearError() }) {
Text("OK")
}
}
)
}
Permission flow
The most common error is PermissionDenied. Here's a complete permission handling flow:
@Composable
fun RecorderWithPermissionFlow() {
val recorderState = rememberRecorderState()
var showSettings by remember { mutableStateOf(false) }
when {
recorderState.needsPermission -> {
// First time: request permission
PermissionPrompt(
onRequest = { recorderState.requestPermission() }
)
}
recorderState.error is AudioError.PermissionDenied -> {
// Denied: suggest settings
PermissionDeniedPrompt(
onOpenSettings = { showSettings = true },
onDismiss = { recorderState.clearError() }
)
}
else -> {
// Ready to record
RecorderUI(recorderState)
}
}
}