chenhg5/cc-connect · error

qwen tts: parse response: %w

Error message

qwen tts: parse response: %w

What it means

After a 200 status, Synthesize unmarshals the JSON body into the result struct (code/message/output.audio.url). If the body is not valid JSON or doesn't match the expected shape, it returns 'qwen tts: parse response'. The response succeeded at HTTP level but is not the documented envelope.

Source

Thrown at core/tts.go:170

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, "", fmt.Errorf("qwen tts: read response: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, "", fmt.Errorf("qwen tts API %d: %s", resp.StatusCode, body)
	}

	var result struct {
		Code    string `json:"code"`
		Message string `json:"message"`
		Output  struct {
			Audio struct {
				URL string `json:"url"`
			} `json:"audio"`
		} `json:"output"`
	}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, "", fmt.Errorf("qwen tts: parse response: %w", err)
	}
	if result.Code != "" {
		return nil, "", fmt.Errorf("qwen tts API error %s: %s", result.Code, result.Message)
	}
	if result.Output.Audio.URL == "" {
		return nil, "", fmt.Errorf("qwen tts: empty audio URL in response")
	}

	// Download WAV from temporary URL
	audioReq, err := http.NewRequestWithContext(ctx, http.MethodGet, result.Output.Audio.URL, nil)
	if err != nil {
		return nil, "", fmt.Errorf("qwen tts: create download request: %w", err)
	}
	audioResp, err := q.Client.Do(audioReq)
	if err != nil {
		return nil, "", fmt.Errorf("qwen tts: download audio: %w", err)
	}
	defer audioResp.Body.Close()

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Log the raw body on this failure to see what was actually returned.
  2. Check whether a proxy/intermediary is rewriting the response (HTML login pages, WAF blocks).
  3. Verify the BaseURL points at the JSON API endpoint, not a console or redirect URL.
  4. Confirm the provider API version still matches the response schema in core/tts.go.

Example fix

// before
// silent assumption: body is valid JSON
// after
if !json.Valid(body) {
    slog.Error("qwen tts non-JSON body", "prefix", string(body[:min(200, len(body))]))
}
if err := json.Unmarshal(body, &result); err != nil { ... }
Defensive patterns

Strategy: try-catch

Type guard

func isParseResponseError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "qwen tts: parse response")
}

Try / catch

audio, _, err := q.Synthesize(ctx, text, opts)
if isParseResponseError(err) {
    slog.Error("qwen tts returned non-JSON body — check proxy/endpoint", "err", err)
    return fmt.Errorf("TTS service returned an unexpected response")
}

Prevention

When it happens

Trigger: json.Unmarshal(body, &result) fails: body is HTML (proxy/auth portal page), truncated JSON, gzip/content-type mismatch, or an undocumented error envelope shape.

Common situations: A captive portal or corporate proxy returning HTML with 200; an API gateway returning a JSON error with a different field layout; binary/garbage body due to missing Accept-Encoding handling; provider API version drift changing the response schema.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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