chenhg5/cc-connect · warning

qwen asr: empty choices in response

Error message

qwen asr: empty choices in response

What it means

Thrown by QwenSTT.Transcribe in core/speech.go when the Qwen ASR response parses as valid JSON but the 'choices' array is empty. The API accepted the request and returned 200 with well-formed JSON, yet produced no transcription candidate.

Source

Thrown at core/speech.go:197

		return "", fmt.Errorf("read response: %w", err)
	}

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

	var result struct {
		Choices []struct {
			Message struct {
				Content string `json:"content"`
			} `json:"message"`
		} `json:"choices"`
	}
	if err := json.Unmarshal(body, &result); err != nil {
		return "", fmt.Errorf("parse response: %w", err)
	}
	if len(result.Choices) == 0 {
		return "", fmt.Errorf("qwen asr: empty choices in response")
	}

	return strings.TrimSpace(result.Choices[0].Message.Content), nil
}

// GeminiSTT implements SpeechToText using the Google Gemini API.
// Audio is sent as inline_data (base64) in the contents array against the
// generateContent endpoint; the API key is sent via the x-goog-api-key header.
type GeminiSTT struct {
	APIKey  string
	Model   string
	BaseURL string // internal; defaults to Google API, overridable for testing
	Client  *http.Client
}

func NewGeminiSTT(apiKey, model string) *GeminiSTT {
	if model == "" {
		model = "gemini-flash-latest"

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check the audio file is non-empty and contains actual speech.
  2. Convert the audio to a widely supported format (mp3/wav) via ConvertAudioToMP3 before sending.
  3. Return a user-friendly 'no speech detected' message instead of treating it as a system failure.
  4. Try a different Qwen audio model if the current one consistently yields empty choices.
  5. Inspect the full response body (log it) for any finish_reason or filtering metadata.
Defensive patterns

Strategy: fallback

Validate before calling

info, err := os.Stat(audioPath)
if err != nil || info.Size() < 1024 { return errors.New("audio file too small or missing") }

Try / catch

text, err := stt.Transcribe(ctx, audio)
if err != nil {
    if strings.Contains(err.Error(), "empty choices") {
        return "", ErrNoSpeechDetected // treat as 'no speech', not a crash
    }
    return err
}

Prevention

When it happens

Trigger: Qwen returns HTTP 200 with choices:[] — typically when the audio contained no recognizable speech, the audio format was unsupported/mostly silence, or the model refused/filtered the content.

Common situations: User sends a voice message that is pure noise or silence; audio codec Qwen can't decode properly; content filter triggered; a model update changed behavior for edge-case inputs.

Understand the failure class

Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/8aaa16fdbaddd11f. Report an issue: GitHub.