Billionmail/BillionMail · error
voice clone: %w
Error message
voice clone: %w
What it means
resolveVoiceID wraps the error from withRetry(CloneVoice(...)) — the Cartesia voice-cloning API call for VOICE_SAMPLE_URL failed after all retries (3 attempts with 5s/15s/45s backoff). This is an external-API failure: the retry layer has already exhausted attempts, so the underlying error reflects persistent failure of the clone request, not a transient blip. The job fails at the voice phase and no voice ID is available for TTS.
Source
Thrown at core/internal/service/video_gen/orchestrator.go:387
if part != "" {
m[part] = true
}
}
return m
}
// resolveVoiceID determines which Cartesia voice ID to use.
// If VOICE_SAMPLE_URL is set, clones a voice. Otherwise uses VOICE_DEFAULT_ID.
func resolveVoiceID(ctx context.Context, cfg VoiceConfig) (string, error) {
sampleURL := os.Getenv("VOICE_SAMPLE_URL")
defaultID := os.Getenv("VOICE_DEFAULT_ID")
if sampleURL != "" {
resp, err := withRetry(func() (*VoiceCloneResponse, error) {
return CloneVoice(ctx, cfg, "sender-voice", sampleURL)
})
if err != nil {
return "", fmt.Errorf("voice clone: %w", err)
}
return resp.ID, nil
}
if defaultID != "" {
return defaultID, nil
}
return "", fmt.Errorf("no voice configured: set VOICE_SAMPLE_URL or VOICE_DEFAULT_ID")
}
// pollLipSync polls lip sync status until completed or timeout.
func pollLipSync(ctx context.Context, cfg LipSyncConfig, jobID string) (string, error) {
deadline := time.Now().Add(lipSyncTimeout)
for {
if time.Now().After(deadline) {
return "", fmt.Errorf("lip sync timed out after %v", lipSyncTimeout)
}View on GitHub (pinned to fc36c76c05)
Solutions
- Read the wrapped error for the HTTP status/code and check CARTESIA_API_KEY is set and valid for the environment
- Verify VOICE_SAMPLE_URL is publicly fetchable and returns a supported audio format (curl -I the URL)
- Confirm the voice sample meets Cartesia's duration/format requirements and re-encode if needed
- Set VOICE_DEFAULT_ID as a fallback so the pipeline uses a pre-cloned voice when cloning fails
- Check Cartesia service status/quota; rerun the job once the API is healthy
Example fix
// before
voiceID, err := resolveVoiceID(ctx, voiceCfg)
if err != nil {
markFailed(ctx, job.ID, "voice", err)
return err
}
// after
voiceID, err := resolveVoiceID(ctx, voiceCfg)
if err != nil {
if fallback := os.Getenv("VOICE_DEFAULT_ID"); fallback != "" {
g.Log().Warningf(ctx, "voice clone failed (%v), falling back to default voice", err)
voiceID = fallback
} else {
markFailed(ctx, job.ID, "voice", err)
return err
}
} Defensive patterns
Strategy: fallback
Validate before calling
if os.Getenv("CARTESIA_API_KEY") == "" {
return fmt.Errorf("voice phase skipped: CARTESIA_API_KEY not configured")
}
resp, err := http.Head(os.Getenv("VOICE_SAMPLE_URL"))
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("VOICE_SAMPLE_URL not reachable: status=%v err=%v", statusOf(resp), err)
} Try / catch
voiceID, err := resolveVoiceID(ctx, voiceCfg)
if err != nil {
var apiErr *APIError
if errors.As(err, &apiErr) {
g.Log().Errorf(ctx, "cartesia clone failed status=%d body=%s", apiErr.StatusCode, apiErr.Body)
}
// fall back to default voice or fail with phase context
if def := os.Getenv("VOICE_DEFAULT_ID"); def != "" {
voiceID = def
} else {
return err
}
} Prevention
- Pre-clone the sender voice once at setup and store VOICE_DEFAULT_ID instead of cloning per job
- Validate VOICE_SAMPLE_URL with a HEAD request at startup or before each batch
- Rotate and test CARTESIA_API_KEY per environment; never share keys across prod/staging
- Monitor Cartesia 4xx/5xx rates and alert before jobs start failing en masse
- Keep the withRetry backoff, but distinguish retryable (5xx/429) from permanent (400/401) failures to fail fast
When it happens
Trigger: CloneVoice(ctx, cfg, "sender-voice", sampleURL) fails on every attempt: CARTESIA_API_KEY missing/invalid (401/403), VOICE_SAMPLE_URL unreachable or not an audio file (400/422), sample too long/short for Cartesia, rate limits beyond the 5 rps limiter, or network egress blocked.
Common situations: VOICE_SAMPLE_URL pointing at a private/404 URL or an HTML error page instead of a WAV/MP3; expired or wrong-env Cartesia API key; server without outbound internet access; sample audio in an unsupported codec; Cartesia outage or plan quota exhausted.
Related errors
- cartesia clone API error %d: %s
- decode clone response: %w
- cartesia TTS API error %d: %s
- invalid start_date: %v
- invalid end_date: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/0c1ce1ef3a32f1ce.
Report an issue: GitHub.