Billionmail/BillionMail · error
write audio data: %w
Error message
write audio data: %w
What it means
Once the WAV file is created, TextToSpeech streams the audio with io.Copy(f, resp.Body). If the copy fails — the Cartesia connection drops mid-transfer, a read timeout on the response body, or a disk write error — the cause is wrapped as 'write audio data'. The result is a partial or zero-byte WAV file at outPath.
Source
Thrown at core/internal/service/video_gen/voice.go:206
if err != nil {
return "", fmt.Errorf("cartesia TTS API call: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("cartesia TTS API error %d: %s", resp.StatusCode, string(body))
}
outPath := filepath.Join(cfg.OutputDir, filename)
f, err := os.Create(outPath)
if err != nil {
return "", fmt.Errorf("create output file: %w", err)
}
defer f.Close()
if _, err := io.Copy(f, resp.Body); err != nil {
return "", fmt.Errorf("write audio data: %w", err)
}
return outPath, nil
}
View on GitHub (pinned to fc36c76c05)
Solutions
- Inspect the wrapped error: read/write vs network; retry on network-side failures.
- Increase HTTP client response-header/body read timeouts for long audio.
- Check free disk space and filesystem health on OutputDir.
- Delete the partial file on failure so callers don't consume truncated audio.
- Verify the produced file is non-empty and a valid WAV before returning.
Example fix
// before
if _, err := io.Copy(f, resp.Body); err != nil {
return "", fmt.Errorf("write audio data: %w", err)
}
return outPath, nil
// after
if _, err := io.Copy(f, resp.Body); err != nil {
f.Close()
os.Remove(outPath)
return "", fmt.Errorf("write audio data: %w", err)
}
if err := f.Close(); err != nil {
os.Remove(outPath)
return "", fmt.Errorf("close audio file: %w", err)
}
return outPath, nil Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check: enough free space for the expected audio (rough estimate)
const minFreeBytes = 10 * 1024 * 1024
if st, err := os.Statfs(cfg.OutputDir); err == nil {
// syscall-free alternative:
_ = st
}
var st syscall.Statfs_t
if err := syscall.Statfs(cfg.OutputDir, &st); err == nil {
free := st.Bavail * uint64(st.Bsize)
if free < minFreeBytes {
return errors.New("insufficient disk space for audio output")
}
} Try / catch
path, err := video_gen.TextToSpeech(ctx, cfg, voiceID, transcript, filename)
if err != nil {
if strings.Contains(err.Error(), "write audio data") {
os.Remove(path) // clean partial file if any
return fmt.Errorf("audio transfer interrupted, retry: %w", err)
}
return err
}
if info, err := os.Stat(path); err != nil || info.Size() == 0 {
return errors.New("audio file missing or empty after generation")
} Prevention
- Set generous read timeouts for large/long audio responses
- Always delete partial files on failure
- Verify output file size/WAV header before downstream use
- Monitor disk space and LB idle timeouts for long streaming responses
When it happens
Trigger: Cartesia closes the connection before the full audio streams, RateLimitedClient read timeout expires mid-body, ctx is cancelled during transfer, or the disk fills while writing.
Common situations: Long transcripts producing large audio over flaky networks, aggressive HTTP client timeouts, small disk volumes in containers, LB/proxy idle timeouts cutting long responses.
Related errors
- error reading project configuration file: %v
- error writing project configuration file: %v
- error writing knowledge base file: %v
- error reading knowledge base directory: %v
- error saving knowledge base: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/e58d8808137e7cd4.
Report an issue: GitHub.