Kuberwastaken/claurst · error · anyhow::Error

Transcription API returned

Error message

Transcription API returned {}: {}

What it means

transcribe_audio sends the recorded audio to the configured Whisper-compatible endpoint and, on any non-success HTTP status, includes the status code and response body in the error. This surfaces upstream API failures (auth, rate limits, malformed requests, server errors) verbatim so the caller can diagnose them.

Solutions

  1. Read the status and body in the error to identify the cause (401 → fix key, 429 → back off, 404 → fix endpoint URL).
  2. Verify OPENAI_API_KEY validity and quota.
  3. Confirm WHISPER_ENDPOINT_URL points at the correct route (e.g. /inference for whisper.cpp).
  4. Add retry with backoff for 5xx/429 statuses.

Example fix

// before
let text = voice.record_and_transcribe().await?;
// after
match voice.record_and_transcribe().await {
    Ok(t) => t,
    Err(e) if e.to_string().contains("429") => {
        tokio::time::sleep(Duration::from_secs(5)).await;
        voice.record_and_transcribe().await?
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: retry

Validate before calling

let url = std::env::var("WHISPER_ENDPOINT_URL")
    .unwrap_or_else(|_| "https://api.openai.com/v1/audio/transcriptions".into());
// sanity-check reachability before recording
reqwest::get(url).await?.error_for_status()?;

Try / catch

match voice.record_and_transcribe().await {
    Err(e) if e.to_string().contains("returned 429") => {
        backoff_retry().await
    }
    Err(e) if e.to_string().contains("returned 401") => {
        prompt_for_api_key()?; voice.record_and_transcribe().await?
    }
    other => other,
}

Prevention

When it happens

Trigger: POSTing audio to the transcription endpoint and receiving 4xx/5xx: invalid API key (401), quota exhausted (429), too-large payload (413), wrong endpoint URL (404), or transcription service outage (500/502).

Common situations: Expired/revoked OPENAI_API_KEY; local Whisper server not speaking the expected route; audio file exceeding provider size limit; rate limiting after heavy voice usage; typo'd WHISPER_ENDPOINT_URL.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/4d270fef66468bb0. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/core/src/voice.rs:534

    let mut form = reqwest::multipart::Form::new()
        .text("model", model.to_string())
        .part("file", file_part);

    if let Some(lang) = language {
        form = form.text("language", lang.to_string());
    }

    let response = client
        .post(url)
        .bearer_auth(api_key)
        .multipart(form)
        .send()
        .await?;

    let status = response.status();
    if !status.is_success() {
        let body = response.text().await.unwrap_or_default();
        return Err(anyhow::anyhow!(
            "Transcription API returned {}: {}",
            status,
            body
        ));
    }

    let json: serde_json::Value = response.json().await?;
    let text = json["text"]
        .as_str()
        .unwrap_or("")
        .trim()
        .to_string();
    Ok(text)
}

// ---------------------------------------------------------------------------
// Global singleton
// ---------------------------------------------------------------------------

View on GitHub (pinned to b0637c97ec)