chenhg5/cc-connect · error
qwen tts: read audio: %w
Error message
qwen tts: read audio: %w
What it means
After the WAV download response is received (core/tts.go:192), QwenTTS.Synthesize reads the whole body with io.ReadAll. This error wraps a failure while streaming the response body — the connection dropped, the server closed mid-transfer, or a body-limited reader hit its cap.
Source
Thrown at core/tts.go:192
}
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()
wavData, err := io.ReadAll(audioResp.Body)
if err != nil {
return nil, "", fmt.Errorf("qwen tts: read audio: %w", err)
}
return wavData, "wav", nil
}
// ──────────────────────────────────────────────────────────────
// OpenAITTS — OpenAI-compatible TTS implementation (P1)
// ──────────────────────────────────────────────────────────────
// OpenAITTS implements TextToSpeech using the OpenAI /v1/audio/speech API.
type OpenAITTS struct {
APIKey string
BaseURL string
Model string
Client *http.Client
}
// NewOpenAITTS creates a new OpenAITTS instance.
func NewOpenAITTS(apiKey, baseURL, model string, client *http.Client) *OpenAITTS {View on GitHub (pinned to 4000b2338a)
Solutions
- Retry Synthesize; transient body-read interruptions are usually temporary
- Verify the ctx is not cancelled mid-operation and that no middleware limits the response body size
- Increase the client timeout and check proxy idle/connection limits
- Wrap the body reader with retry/resume logic if downloads are routinely large
Example fix
// before
wavData, err := io.ReadAll(audioResp.Body)
if err != nil {
return nil, "", fmt.Errorf("qwen tts: read audio: %w", err)
}
// after
wavData, err := io.ReadAll(audioResp.Body)
if err != nil {
return nil, "", fmt.Errorf("qwen tts: read audio (%d bytes read): %w", len(wavData), err)
}
if audioResp.StatusCode != http.StatusOK {
return nil, "", fmt.Errorf("qwen tts: audio download HTTP %d", audioResp.StatusCode)
} Defensive patterns
Strategy: retry
Validate before calling
// Enforce a sane response size cap before parsing: const maxWAV = 20 << 20 // 20 MiB limited := io.LimitReader(audioResp.Body, maxWAV+1)
Try / catch
wav, format, err := tts.Synthesize(ctx, text, opts)
if err != nil && strings.Contains(err.Error(), "read audio") {
// transient stream failure — retry once, then degrade gracefully
if wav, format, err = tts.Synthesize(ctx, text, opts); err != nil {
return fmt.Errorf("tts unavailable: %w", err)
}
} Prevention
- Avoid cancelling the context mid-synthesis; scope ctx to the whole Synthesize call
- Increase client timeout for large texts producing long audio
- Route downloads through stable network paths, not flaky proxies
When it happens
Trigger: Connection reset while reading the audio bytes, server-side abort/timeout mid-stream, context cancellation firing during the read, or a Body limited by http.MaxBytesReader that exceeded its limit.
Common situations: Large audio files over flaky mobile/office networks, an intermediate proxy terminating long transfers, or surrounding code cancelling ctx partway through synthesis.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- qwen tts: request: %w
- qwen tts: read response: %w
- qwen tts: download audio: %w
- read body from %s: %w
- read body from %s: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/21da907b5de11270.
Report an issue: GitHub.