Billionmail/BillionMail · error

cartesia clone API call: %w

Error message

cartesia clone API call: %w

What it means

CloneVoice executes the prepared Cartesia clone request via cfg.doHTTP and wraps client-side transport failures with 'cartesia clone API call: %w'. This error fires before any HTTP status check — it means the request never completed, not that the API returned an error payload (non-200 responses produce the separate 'cartesia clone API error %d' error).

Source

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

// CloneVoice creates a cloned voice from an audio sample URL via Cartesia API.
func CloneVoice(ctx context.Context, cfg VoiceConfig, name, audioURL string) (*VoiceCloneResponse, error) {
	req := VoiceCloneRequest{
		Name:        name,
		Description: fmt.Sprintf("Cloned voice for %s", name),
		Mode:        "url",
		AudioURL:    audioURL,
		Language:    "en",
	}

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

	resp, err := cfg.doHTTP(httpReq)
	if err != nil {
		return nil, fmt.Errorf("cartesia clone API call: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("cartesia clone API error %d: %s", resp.StatusCode, string(body))
	}

	var result VoiceCloneResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("decode clone response: %w", err)
	}
	return &result, nil
}

// TextToSpeech generates audio from text using a Cartesia voice.
// Returns the path to the output WAV file.
func TextToSpeech(ctx context.Context, cfg VoiceConfig, voiceID, transcript, filename string) (string, error) {

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Unwrap with errors.As(err, &urlErr) and check errors.Is(err, context.DeadlineExceeded)/context.Canceled to classify timeout vs shutdown vs network
  2. Verify outbound connectivity to the Cartesia host (curl https://api.cartesia.ai from the same container)
  3. Increase the context timeout for large voice sample uploads, and check the base URL scheme/host
  4. Retry once with backoff for transient network errors before failing the pipeline
  5. Confirm the API key is valid only if the request completes with a non-200 status (different error path)

Example fix

// before
resp, err := cfg.doHTTP(httpReq)
if err != nil {
    return nil, fmt.Errorf("cartesia clone API call: %w", err)
}
// after
resp, err := cfg.doHTTP(httpReq)
if err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
        return nil, fmt.Errorf("cartesia clone call cancelled or timed out: %w", err)
    }
    var urlErr *url.Error
    if errors.As(err, &urlErr) && isTransient(urlErr) {
        resp, err = retryWithBackoff(httpReq)
        if err != nil {
            return nil, fmt.Errorf("cartesia clone API call after retry: %w", err)
        }
        return resp, nil
    }
    return nil, fmt.Errorf("cartesia clone API call: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ctx.Err() != nil {
    return fmt.Errorf("clone aborted before call: %w", ctx.Err())
}
if netErr := checkConnectivity("https://api.cartesia.ai"); netErr != nil {
    return fmt.Errorf("cartesia unreachable: %w", netErr)
}

Try / catch

clone, err := video_gen.CloneVoice(ctx, cfg, req)
if err != nil {
    var urlErr *url.Error
    switch {
    case errors.Is(err, context.Canceled):
        // caller cancelled; no retry
    case errors.Is(err, context.DeadlineExceeded):
        // increase timeout or shrink sample payload, then retry
    case errors.As(err, &urlErr) && isTransientNetError(urlErr):
        // retry with exponential backoff
    default:
        return fmt.Errorf("voice clone failed: %w", err)
    }
}

Prevention

When it happens

Trigger: cfg.doHTTP(httpReq) returns a *url.Error: DNS resolution failure, connection refused/timeout, TLS handshake failure, or the request context (attached via httpReq.WithContext(ctx)) was cancelled before a response arrived.

Common situations: No outbound internet or blocked egress to api.cartesia.ai; DNS outage; expired/invalid API key is NOT this error (that returns non-200 and hits the status branch); pipeline shutdown or per-call timeout cancelling ctx mid-request.

Related errors


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