Billionmail/BillionMail · error
lip sync timed out after
Error message
lip sync timed out after %v
What it means
pollLipSync polls the external lip-sync provider until the job completes, but stops after lipSyncTimeout has elapsed. If the provider still has not finished by the deadline, the poll loop aborts with this error.
Solutions
- Retry RunPipeline's lip-sync stage; transient provider backlogs usually resolve
- Increase lipSyncTimeout to a value appropriate for the video length
- Verify the job ID is valid and the lip-sync submission succeeded (check upstream logs)
- Check provider status/dashboard for ongoing incidents
Example fix
// before const lipSyncTimeout = 10 * time.Minute // after const lipSyncTimeout = 30 * time.Minute // long videos need more headroom
Defensive patterns
Strategy: retry
Validate before calling
if lipSyncTimeout < estimatedProcessingTime(videoDurationSec) {
return fmt.Errorf("lipSyncTimeout %v too short for %ds video", lipSyncTimeout, videoDurationSec)
} Try / catch
url, err := pollLipSync(ctx, cfg, jobID)
if err != nil && strings.Contains(err.Error(), "timed out") {
// check the job once more directly — it may have completed after the deadline
return CheckLipSyncStatus(ctx, cfg, jobID)
} Prevention
- Size lipSyncTimeout relative to input media duration
- Alert on repeated timeouts — they usually indicate provider degradation
- Make the timeout configurable via env so it can be tuned without a redeploy
- Poll less frequently with backoff to avoid provider throttling
When it happens
Trigger: CheckLipSyncStatus keeps returning a non-terminal status (processing/queued) past lipSyncTimeout while RunPipeline waits; typically for long videos, provider congestion, or a stuck job.
Common situations: Provider outage or degraded service; very long source audio; job ID whose upstream request silently failed so it never completes; timeout too short for large inputs.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- cartesia clone API call
- cartesia TTS API call
- check lip sync status
- lip sync completed but no video URL
- lip sync failed
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/8b3f824b825328fa.
Report an issue: GitHub.
Appendix: source
Thrown at core/internal/service/video_gen/orchestrator.go:404
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)
}
resp, err := CheckLipSyncStatus(ctx, cfg, jobID)
if err != nil {
return "", fmt.Errorf("check lip sync status: %w", err)
}
switch resp.Status {
case "completed":
if resp.VideoURL == "" {
return "", fmt.Errorf("lip sync completed but no video URL")
}
return resp.VideoURL, nil
case "failed":
return "", fmt.Errorf("lip sync failed: %s", resp.Error)
}
time.Sleep(lipSyncPollDelay)View on GitHub (pinned to fc36c76c05)