{"record":{"id":"5b63b34fc0510e85","repo":"zeroclaw-labs/zeroclaw","slug":"google-stt-api-error","errorCode":null,"errorMessage":"Google STT API error ({}): {}","messagePattern":"Google STT API error \\((.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/transcription.rs","lineNumber":736,"sourceCode":"                \"content\": audio_content,\n            }\n        });\n\n        let resp = self\n            .build_request(&request_body)?\n            .send()\n            .await\n            .context(\"Failed to send transcription request to Google STT\")?;\n\n        let status = resp.status();\n        let body: serde_json::Value = resp\n            .json()\n            .await\n            .context(\"Failed to parse Google STT response\")?;\n\n        if !status.is_success() {\n            let error_msg = body[\"error\"][\"message\"].as_str().unwrap_or(\"unknown error\");\n            bail!(\"Google STT API error ({}): {}\", status, error_msg);\n        }\n\n        let text = body[\"results\"][0][\"alternatives\"][0][\"transcript\"]\n            .as_str()\n            .unwrap_or(\"\")\n            .to_string();\n\n        Ok(text)\n    }\n}\n\n// ── LocalWhisperProvider ────────────────────────────────────────\n\npub struct LocalWhisperProvider {\n    alias: String,\n    url: String,\n    bearer_token: Option<String>,\n    max_audio_bytes: usize,","sourceCodeStart":718,"sourceCodeEnd":754,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/transcription.rs#L718-L754","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["400 with 'Sync input too long' — split audio into sub-minute chunks or switch to a provider that accepts long files (deepgram, assemblyai)","401/403 — check the api_key in [transcription.google] and remove API restrictions that block the Speech-to-Text API","Verify the extension matches the real codec — the encoding enum is chosen purely from the file extension","429 — check Cloud Console quota for the Speech-to-Text API and retry with backoff"],"exampleFix":"# before: 5-minute voice note -> google provider -> 400 Sync input too long\n\n# after: split into <60s chunks\nffmpeg -i note.ogg -f segment -segment_time 55 -ar 16000 -ac 1 -c:a flac chunk_%02d.flac\n# then transcribe chunks and join the texts","handlingStrategy":"try-catch","validationCode":"// Google v1 speech:recognize is synchronous: cap audio at ~1 minute.\n// Split or pick another provider before calling for longer input.\nfn google_sync_recognize_ok(duration_secs: f64, bytes: usize) -> bool {\n    duration_secs <= 55.0 && bytes <= 10 * 1024 * 1024\n}","typeGuard":null,"tryCatchPattern":"match provider.transcribe(&audio, name).await {\n    Ok(text) => Ok(text),\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"Sync input too long\") {\n            Err(anyhow!(\"audio too long for google sync recognize; split it or use deepgram/assemblyai\"))\n        } else if msg.contains(\"403\") {\n            Err(anyhow!(\"google api key invalid or restricted for speech-to-text\"))\n        } else {\n            Err(e)\n        }\n    }\n}","preventionTips":["Keep Google-provider inputs under ~1 minute / 10 MB — the sync v1 endpoint has hard limits","In Cloud Console, restrict the API key only to the Speech-to-Text API, not by IP/referrer unless the runtime egress matches","Ensure the file extension matches the real codec — the encoding is inferred from the name"],"tags":["google","stt","http-status","quota","transcription","audio-length"],"backgroundTag":"speech-to-text-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}