{"record":{"id":"8cedc6f890fd509b","repo":"chenhg5/cc-connect","slug":"parse-response-w","errorCode":null,"errorMessage":"parse response: %w","messagePattern":"parse response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/speech.go","lineNumber":194,"sourceCode":"\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\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","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/speech.go#L176-L212","documentation":"Thrown by QwenSTT.Transcribe in core/speech.go when the JSON response body from the Qwen ASR API cannot be unmarshaled into the expected {choices:[{message:{content}}]} structure. The underlying json.Unmarshal error is wrapped with 'parse response:'. Indicates the server replied with valid HTTP 200 but a body that is not the expected JSON shape (often HTML from a proxy or an error object).","triggerScenarios":"json.Unmarshal fails on the 200-OK response body: body is HTML (auth portal/proxy page), truncated response, or a JSON object with different field names because BaseURL points at a non-OpenAI-compatible Qwen endpoint.","commonSituations":"Corporate proxy or gateway returning an HTML login page; BaseURL misconfigured so responses come from a different API shape; network middleware corrupting the body; Qwen changing response schema for a newer model.","solutions":["Log/inspect the raw response body to see what was actually returned.","Verify BaseURL uses the OpenAI-compatible endpoint (…/compatible-mode/v1) so the response has a 'choices' array.","Check for proxies/gateways intercepting the request (corporate MITM, auth walls).","Retry the request — truncated bodies are often transient network issues.","If the model's schema changed, update the parsing struct in core/speech.go."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"text, err := stt.Transcribe(ctx, audio)\nif err != nil {\n    if strings.Contains(err.Error(), \"parse response\") {\n        slog.Warn(\"qwen asr: unexpected body, likely proxy/endpoint issue\", \"err\", err)\n        return \"\", ErrUnexpectedResponse\n    }\n    return err\n}","preventionTips":["Use the official OpenAI-compatible Qwen endpoint so responses always have 'choices'.","Avoid proxies/gateways that can inject HTML error pages.","Log raw bodies on parse failure to speed diagnosis.","Keep the response struct in sync with the provider's documented schema."],"tags":["json","speech-to-text","qwen","parsing"],"backgroundTag":"json-unmarshal-failed","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"}