Billionmail/BillionMail · error

lip sync timed out after %v

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.

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)

Solutions

  1. Retry RunPipeline's lip-sync stage; transient provider backlogs usually resolve
  2. Increase lipSyncTimeout to a value appropriate for the video length
  3. Verify the job ID is valid and the lip-sync submission succeeded (check upstream logs)
  4. 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

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

Related errors


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