zeroclaw-labs/zeroclaw · error

Google STT API error ({}): {}

Error message

Google STT API error ({}): {}

What it means

GoogleSttProvider::transcribe() POSTs base64 audio to speech.googleapis.com/v1/speech:recognize and bails on non-2xx, embedding the status plus the body's error.message. The provider uses the synchronous v1 recognize endpoint, which has hard limits (roughly one minute / ~10 MB of audio per request), so long audio fails here even when the format is supported. The key is sent via the x-goog-api-key header.

Source

Thrown at crates/zeroclaw-channels/src/transcription.rs:736

                "content": audio_content,
            }
        });

        let resp = self
            .build_request(&request_body)?
            .send()
            .await
            .context("Failed to send transcription request to Google STT")?;

        let status = resp.status();
        let body: serde_json::Value = resp
            .json()
            .await
            .context("Failed to parse Google STT response")?;

        if !status.is_success() {
            let error_msg = body["error"]["message"].as_str().unwrap_or("unknown error");
            bail!("Google STT API error ({}): {}", status, error_msg);
        }

        let text = body["results"][0]["alternatives"][0]["transcript"]
            .as_str()
            .unwrap_or("")
            .to_string();

        Ok(text)
    }
}

// ── LocalWhisperProvider ────────────────────────────────────────

pub struct LocalWhisperProvider {
    alias: String,
    url: String,
    bearer_token: Option<String>,
    max_audio_bytes: usize,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. 400 with 'Sync input too long' — split audio into sub-minute chunks or switch to a provider that accepts long files (deepgram, assemblyai)
  2. 401/403 — check the api_key in [transcription.google] and remove API restrictions that block the Speech-to-Text API
  3. Verify the extension matches the real codec — the encoding enum is chosen purely from the file extension
  4. 429 — check Cloud Console quota for the Speech-to-Text API and retry with backoff

Example fix

# before: 5-minute voice note -> google provider -> 400 Sync input too long

# after: split into <60s chunks
ffmpeg -i note.ogg -f segment -segment_time 55 -ar 16000 -ac 1 -c:a flac chunk_%02d.flac
# then transcribe chunks and join the texts
Defensive patterns

Strategy: try-catch

Validate before calling

// Google v1 speech:recognize is synchronous: cap audio at ~1 minute.
// Split or pick another provider before calling for longer input.
fn google_sync_recognize_ok(duration_secs: f64, bytes: usize) -> bool {
    duration_secs <= 55.0 && bytes <= 10 * 1024 * 1024
}

Try / catch

match provider.transcribe(&audio, name).await {
    Ok(text) => Ok(text),
    Err(e) => {
        let msg = e.to_string();
        if msg.contains("Sync input too long") {
            Err(anyhow!("audio too long for google sync recognize; split it or use deepgram/assemblyai"))
        } else if msg.contains("403") {
            Err(anyhow!("google api key invalid or restricted for speech-to-text"))
        } else {
            Err(e)
        }
    }
}

Prevention

When it happens

Trigger: POST speech:recognize returns non-success: 400 'Sync input too long' for audio over ~1 minute, encoding/extension mismatch (declared FLAC but bytes are MP3), or invalid request shape; 403 API key invalid or restricted (HTTP-referrer/IP restrictions on the key); 429 quota exceeded.

Common situations: Long voice notes (>1 min) sent to the google provider — by far the most common hit; Google Cloud API key with restrictions that exclude the speech endpoint; wrong [transcription.google] api_key; declared encoding not matching actual bytes after a transcoding bug.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/5b63b34fc0510e85. Report an issue: GitHub.