diff --git a/src/mcspeak/audio.py b/src/mcspeak/audio.py index 4574649..6c66d50 100644 --- a/src/mcspeak/audio.py +++ b/src/mcspeak/audio.py @@ -331,7 +331,8 @@ async def record_audio_until_silence( if not pcm_buf: raise PlaybackError( - f"VAD recording captured no audio. Check source '{source or 'default'}'." + f"No audio captured — is a microphone connected? " + f"(source '{source or 'default'}' produced no samples)" ) # Write the accumulated PCM as a WAV file. diff --git a/src/mcspeak/server.py b/src/mcspeak/server.py index a1fc7c0..5bc9bb2 100644 --- a/src/mcspeak/server.py +++ b/src/mcspeak/server.py @@ -895,7 +895,10 @@ async def listen( warmup_ms=warmup_ms, ) except Exception as e: - return {"error": f"VAD recording failed: {e}"} + # Include the exception TYPE — some (e.g. asyncio.TimeoutError) + # stringify to "", which otherwise hides a missing-mic failure. + detail = str(e) or "no detail (often a disconnected/blocked mic)" + return {"error": f"VAD recording failed ({type(e).__name__}): {detail}"} else: await ctx.info(f"Listening for {duration_seconds:.1f}s (source={source or 'default'})...") try: @@ -904,7 +907,8 @@ async def listen( ready_tone=start_tone, warmup_ms=warmup_ms, ) except Exception as e: - return {"error": f"Recording failed: {e}"} + detail = str(e) or "no detail (often a disconnected/blocked mic)" + return {"error": f"Recording failed ({type(e).__name__}): {detail}"} # "Capture done" tone AFTER pw-record stops. Audible "over and out" # so the user knows they can stop talking and the system is now