chenhg5/cc-connect · error

whisper API %d: %s

Error message

whisper API %d: %s

What it means

The Whisper API returned a non-200 status; Transcribe surfaces the status code and the raw response body. This is the server-side error path: auth failures (401), bad model name (404/400), quota/billing (429), or server errors (5xx).

Source

Thrown at core/speech.go:96

	if err != nil {
		return "", fmt.Errorf("create request: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+w.APIKey)
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := w.Client.Do(req)
	if err != nil {
		return "", fmt.Errorf("whisper request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("whisper API %d: %s", resp.StatusCode, string(body))
	}

	// response_format=text returns plain text; try to handle JSON fallback
	text := strings.TrimSpace(string(body))
	if strings.HasPrefix(text, "{") {
		var jr struct {
			Text string `json:"text"`
		}
		if json.Unmarshal(body, &jr) == nil {
			text = jr.Text
		}
	}
	return text, nil
}

// QwenASR implements SpeechToText using the Qwen ASR model via DashScope's
// OpenAI-compatible chat completions API. Unlike Whisper, audio is sent as a
// base64 data URI inside the messages array.

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the body in the error message — providers include a JSON reason (e.g. invalid_api_key, model_not_found)
  2. For 401, verify the API key is correct, active, and has audio API access
  3. For 404/400 model errors, set Model to one supported by the endpoint (e.g. whisper-large-v3 for Groq)
  4. For 429, add backoff/retry and check quota/billing
  5. For 5xx, retry later or check the provider status page

Example fix

// before (Groq endpoint with OpenAI default model)
NewOpenAIWhisper(key, "https://api.groq.com/openai/v1", "") // defaults to whisper-1 -> 404
// after
NewOpenAIWhisper(key, "https://api.groq.com/openai/v1", "whisper-large-v3")
Defensive patterns

Strategy: try-catch

Validate before calling

if cfg.WhisperAPIKey == "" {
    return fmt.Errorf("missing whisper api_key in config")
}
if !slices.Contains(supportedModels, cfg.WhisperModel) {
    return fmt.Errorf("model %q not supported by endpoint", cfg.WhisperModel)
}

Try / catch

text, err := stt.Transcribe(ctx, audio, format, lang)
if err != nil {
    var apiErr *HTTPStatusError // or parse 'whisper API %d:' prefix
    if strings.Contains(err.Error(), "whisper API 401") {
        slog.Error("whisper auth failed: check api_key")
    } else if strings.Contains(err.Error(), "whisper API 429") {
        // back off and retry
    } else {
        slog.Error("whisper API error", "err", err)
    }
}

Prevention

When it happens

Trigger: Transcribe called with an invalid or expired API key (401), a Model not available on the endpoint (404/400), exceeded rate limits (429), invalid audio format (400), or provider outage (500/503).

Common situations: Missing/typo'd OPENAI_API_KEY or provider key in config; using whisper-1 against Groq which requires 'whisper-large-v3'; billing/quota exhausted; sending an unsupported audio extension to the endpoint.

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 chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/048a5111a36b0691. Report an issue: GitHub.