cjpais/Handy · error · std::io::Error
{error_message}
Error message
{error_message} What it means
The microphone worker thread failed to initialize and its string message is re-raised on the caller's thread. The message originates inside the worker: 'Failed to build input stream: {e}', 'Failed to start microphone stream: {e}', 'Unsupported sample format: {format:?}', or a device-config fetch failure. is_microphone_access_denied() promotes OS permission messages ('Access is denied', 'permission denied', WASAPI 0x80070005) to ErrorKind::PermissionDenied; everything else becomes ErrorKind::Other.
Source
Thrown at src-tauri/src/audio_toolkit/audio/recorder.rs:359
}
}
});
match init_rx.recv() {
Ok(Ok(())) => {
self.device = Some(device);
self.cmd_tx = Some(cmd_tx);
self.worker_handle = Some(worker);
Ok(())
}
Ok(Err(error_message)) => {
let _ = worker.join();
let kind = if is_microphone_access_denied(&error_message) {
std::io::ErrorKind::PermissionDenied
} else {
std::io::ErrorKind::Other
};
Err(Box::new(Error::new(kind, error_message)))
}
Err(recv_error) => {
let _ = worker.join();
Err(Box::new(Error::other(format!(
"Failed to initialize microphone worker: {recv_error}"
))))
}
}
}
/// Queue a recording start and return a one-shot receiver that resolves only
/// after the first real microphone sample chunk has entered the capture path.
/// `Stream::play()` returning is not sufficient: some Bluetooth and USB
/// devices take much longer to begin delivering callbacks.
pub fn start(
&self,
vad_policy: VadPolicy,
) -> Result<mpsc::Receiver<()>, Box<dyn std::error::Error>> {View on GitHub (pinned to c6fa60da2f)
Solutions
- Grant microphone access: macOS System Settings > Privacy & Security > Microphone; Windows Settings > Privacy & Security > Microphone
- Re-select the input device in settings (a stale cached config is dropped on failure, so a retry re-queries the device) and retry
- Unplug/replug the mic or restart it; on Windows, disable exclusive-mode capture by other apps
- If the message mentions an unsupported sample format, change the device's default rate/format in OS sound settings or update the audio driver
Example fix
// before
if let Err(e) = recorder.start() {
eprintln!("{e}");
}
// after — branch on the promoted error kind the start() handshake already produced
if let Err(e) = recorder.start() {
if e.kind() == std::io::ErrorKind::PermissionDenied {
// guide user to OS microphone privacy settings
} else if is_no_input_device_error(&e.to_string()) {
// refresh the device list and prompt re-selection
} else {
log::error!("microphone init failed: {e}");
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: confirm the selected device still exists before opening it
let device_still_present = host
.input_devices()?
.filter_map(|d| d.name().ok())
.any(|name| name == selected_device_name);
if !device_still_present {
// re-prompt device selection before attempting start()
} Type guard
fn is_permission_error(e: &std::io::Error) -> bool {
e.kind() == std::io::ErrorKind::PermissionDenied
} Try / catch
match recorder.start() {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
// surface a "grant microphone access" action pointing at OS privacy settings
}
Err(e) => {
// log; the worker already dropped its stale config cache, so re-select the device and retry once
}
} Prevention
- Check microphone authorization up front (macOS TCC status; Windows privacy policy) before the first recording
- Validate the stored device id against current devices on every start and fall back to the default with a UI notice
- After an open failure, re-query the device config (the code drops its cache) instead of blindly retrying the same open
When it happens
Trigger: start() spawns the worker, which fetches the device config, builds a cpal input stream and calls stream.play(); any of these failing sends Err(msg) over init_tx and this branch re-raises it. Triggers: mic permission denied (macOS TCC, Windows privacy), device unplugged mid-open, device occupied, exotic sample format, or a stale cached config after the device changed rate/format in the OS.
Common situations: First run on macOS without granting Microphone permission; Windows microphone access toggled off in Privacy settings; recording right after unplugging a USB mic; Bluetooth headset switching profile (A2DP to HFP) between enumeration and open; devices exposing sample formats cpal cannot map.
Related errors
- Failed to create AudioRecorder: {}
- No input device found
- Failed to open recorder: {}
- Failed to check Windows microphone permissions:
- Failed to create resampler
AI-assisted analysis of cjpais/Handy@c6fa60da2f (2026-08-17).
Data as JSON: /api/errors/9bd909f50e0238b6.
Report an issue: GitHub.