tinyhumansai/openhuman · error
audio blob is empty
Error message
audio blob is empty
What it means
transcribeCloud() sends a recorded audio blob to the core via JSON-RPC (base64-encoded) for hosted speech-to-text. Before encoding it guards that the blob exists and has size > 0, throwing 'audio blob is empty' otherwise. This is an input validation failure — no network or RPC has happened yet.
Solutions
- Guard at the call site: skip transcription entirely when blob.size === 0 (treat as 'no speech captured') rather than calling the API.
- Ensure MediaRecorder has received at least one dataavailable chunk before stop() — await the recorder's onstop or collect chunks until stop resolves.
- Log recorder state (error/mime/surface) when an empty blob is produced to find the underlying recorder failure.
- For push-to-talk, enforce a minimum hold time or discard sub-threshold recordings with a friendly hint.
Example fix
// before
const text = await transcribeCloud(blob);
// after
if (!blob || blob.size === 0) {
showToast('No speech captured — hold the button a bit longer.');
return;
}
const text = await transcribeCloud(blob); Defensive patterns
Strategy: validation
Validate before calling
function hasAudio(blob: Blob | null | undefined): boolean {
return !!blob && blob.size > 0;
}
if (!hasAudio(blob)) return notifyNoSpeech(); Type guard
function isNonEmptyBlob(b: unknown): b is Blob {
return b instanceof Blob && b.size > 0;
} Prevention
- Await the recorder's onstop and require chunks.length > 0 before building the Blob.
- Guard at the UI boundary so empty captures get friendly feedback, not an exception.
- Log blob.size/mime at capture end during development to catch recorder regressions.
When it happens
Trigger: Calling transcribeCloud(blob) with a zero-length Blob, a Blob constructed from an empty ArrayBuffer, or undefined/null. Happens when MediaRecorder.start() was called but stop() fired before any audio data was produced (instant tap-and-release on push-to-talk), or when a recording session failed silently and produced an empty blob.
Common situations: Push-to-talk UI where the user taps the button faster than MediaRecorder emits its first chunk; recorder error handler swallowed an error and handed an empty blob downstream; race between stop() and dataavailable event; testing with a mock blob of size 0.
Related errors
- audio blob is empty
- voice_not_compiled
- agent ` ` is a `worker` tier and must not list ` ` in its…
- API request failed
- At least one of --expression, --tz, --command, or --name…
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/43ccb244a13c6ac0.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/features/human/voice/sttClient.ts:82
interface CloudTranscribeResult {
text: string;
}
/**
* Transcribe a recorded audio blob via the Rust core's cloud STT proxy.
*
* The blob is read into a base64 string and shipped over JSON-RPC; the core
* decodes it and POSTs `multipart/form-data` to the hosted backend's
* `/openai/v1/audio/transcriptions` endpoint. Going through the core keeps
* the provider API key off the desktop app and reuses the same auth flow as
* `synthesizeSpeech`.
*/
export async function transcribeCloud(
blob: Blob,
opts: CloudTranscribeOptions = {}
): Promise<string> {
if (!blob || blob.size === 0) {
throw new Error('audio blob is empty');
}
const encodeStart = Date.now();
const audio_base64 = await blobToBase64(blob);
const encodeMs = Math.round(Date.now() - encodeStart);
const params: Record<string, unknown> = { audio_base64 };
// MediaRecorder mime types include codec parameters (e.g. `audio/webm;codecs=opus`)
// — the backend's allow-list expects the bare type, so strip the suffix.
const mime = (opts.mimeType ?? blob.type ?? 'audio/webm').split(';')[0].trim() || 'audio/webm';
params.mime_type = mime;
params.file_name = opts.fileName ?? `audio.${guessExtension(mime)}`;
if (opts.model) params.model = opts.model;
if (opts.language) params.language = opts.language;
sttLog(
'transcribe bytes=%d mime=%s base64_ms=%d (b64_size=%d)',
blob.size,
mime,View on GitHub (pinned to 7491200858)