{"record":{"id":"8aaa16fdbaddd11f","repo":"chenhg5/cc-connect","slug":"qwen-asr-empty-choices-in-response","errorCode":null,"errorMessage":"qwen asr: empty choices in response","messagePattern":"qwen asr: empty choices in response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/speech.go","lineNumber":197,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"qwen asr API %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"parse response: %w\", err)\n\t}\n\tif len(result.Choices) == 0 {\n\t\treturn \"\", fmt.Errorf(\"qwen asr: empty choices in response\")\n\t}\n\n\treturn strings.TrimSpace(result.Choices[0].Message.Content), nil\n}\n\n// GeminiSTT implements SpeechToText using the Google Gemini API.\n// Audio is sent as inline_data (base64) in the contents array against the\n// generateContent endpoint; the API key is sent via the x-goog-api-key header.\ntype GeminiSTT struct {\n\tAPIKey  string\n\tModel   string\n\tBaseURL string // internal; defaults to Google API, overridable for testing\n\tClient  *http.Client\n}\n\nfunc NewGeminiSTT(apiKey, model string) *GeminiSTT {\n\tif model == \"\" {\n\t\tmodel = \"gemini-flash-latest\"","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/speech.go#L179-L215","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the audio file is non-empty and contains actual speech.","Convert the audio to a widely supported format (mp3/wav) via ConvertAudioToMP3 before sending.","Return a user-friendly 'no speech detected' message instead of treating it as a system failure.","Try a different Qwen audio model if the current one consistently yields empty choices.","Inspect the full response body (log it) for any finish_reason or filtering metadata."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"info, err := os.Stat(audioPath)\nif err != nil || info.Size() < 1024 { return errors.New(\"audio file too small or missing\") }","typeGuard":null,"tryCatchPattern":"text, err := stt.Transcribe(ctx, audio)\nif err != nil {\n    if strings.Contains(err.Error(), \"empty choices\") {\n        return \"\", ErrNoSpeechDetected // treat as 'no speech', not a crash\n    }\n    return err\n}","preventionTips":["Pre-check audio is non-empty and contains speech (energy/VAD check if possible).","Always convert to a supported format (mp3/wav) before upload.","Treat empty results as user-facing 'no speech detected' instead of a system error.","Log full response bodies to spot filtering metadata."],"tags":["speech-to-text","qwen","empty-response","audio"],"backgroundTag":"empty-api-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}