Billionmail/BillionMail · error

cartesia TTS API call: %w

Error message

cartesia TTS API call: %w

What it means

TextToSpeech posts to Cartesia /tts/bytes via cfg.doHTTP; if the HTTP round trip itself fails — DNS failure, connection refused/timeout, TLS error, or rate-limited client rejection — the error is wrapped as 'cartesia TTS API call'. This is a transport-level failure before any status code is available.

Source

Thrown at core/internal/service/video_gen/voice.go:189

	}

	req := TTSRequest{
		VoiceID:      voiceID,
		Transcript:   transcript,
		ModelID:      "sonic-2",
		OutputFormat: DefaultTTSOutputFormat(),
		Language:     "en",
	}

	httpReq, err := BuildTTSRequest(cfg, req)
	if err != nil {
		return "", err
	}
	httpReq = httpReq.WithContext(ctx)

	resp, err := cfg.doHTTP(httpReq)
	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)
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Inspect the wrapped %w chain for the concrete net error (timeout, refused, TLS).
  2. Confirm outbound HTTPS to api.cartesia.ai works (curl -v https://api.cartesia.ai).
  3. Check ctx deadlines and the RateLimitedClient budget; increase limits or wait.
  4. Verify proxy env vars (HTTP_PROXY/HTTPS_PROXY/NO_PROXY) and CA certificates in the container.
  5. Add retry with backoff for transient timeouts/refusals.

Example fix

// before
resp, err := cfg.doHTTP(httpReq)
if err != nil {
    return "", fmt.Errorf("cartesia TTS API call: %w", err)
}
// after
var netErr net.Error
resp, err := cfg.doHTTP(httpReq)
if err != nil {
    if errors.As(err, &netErr) && netErr.Timeout() {
        return "", retryable(fmt.Errorf("cartesia TTS API call: %w", err))
    }
    return "", fmt.Errorf("cartesia TTS API call: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight before calling TextToSpeech
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
conn, err := net.DialTimeout("tcp", "api.cartesia.ai:443", 5*time.Second)
if err != nil {
    return fmt.Errorf("cartesia unreachable: %w", err)
}
conn.Close()

Try / catch

var netErr net.Error
path, err := video_gen.TextToSpeech(ctx, cfg, voiceID, transcript, filename)
if err != nil {
    if strings.Contains(err.Error(), "cartesia TTS API call") && errors.As(err, &netErr) && netErr.Timeout() {
        // transient: retry with exponential backoff
    }
    return err
}

Prevention

When it happens

Trigger: No network/DNS resolution for api.cartesia.ai, connection timeout exceeded, TLS certificate problems, an HTTP proxy blocking egress, or the RateLimitedClient refusing the request when its budget is exhausted.

Common situations: Egress firewall in Docker/K8s blocking outbound 443, expired system CA bundle, missing HTTP_PROXY/HTTPS_PROXY config, ctx already cancelled/expired before the call, rate limiter configured too tightly.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/ff7eeab7a23cb0b4. Report an issue: GitHub.