Billionmail/BillionMail · error

download lipsync video: %w

Error message

download lipsync video: %w

What it means

DownloadLipSyncVideo fetches the finished lip-sync video from the Sync Labs CDN URL via HTTP GET. This error is returned when the HTTP transport itself fails — the request never produced a response (DNS failure, connection refused/reset, TLS error, or the request context was canceled/timed out). It wraps the underlying net/http error with %w so errors.Is/As (e.g. context.DeadlineExceeded) still work.

Source

Thrown at core/internal/service/video_gen/lipsync.go:174

		return nil, fmt.Errorf("decode lipsync status: %w", err)
	}
	return &result, nil
}

// DownloadLipSyncVideo downloads the completed lip sync video to the output directory.
func DownloadLipSyncVideo(ctx context.Context, cfg LipSyncConfig, videoURL, filename string) (string, error) {
	if err := os.MkdirAll(cfg.OutputDir, 0755); err != nil {
		return "", fmt.Errorf("create output dir: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, "GET", videoURL, nil)
	if err != nil {
		return "", fmt.Errorf("create download request: %w", err)
	}

	resp, err := cfg.doHTTP(req)
	if err != nil {
		return "", fmt.Errorf("download lipsync video: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("download error %d", resp.StatusCode)
	}

	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 video data: %w", err)
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check outbound connectivity and DNS from the host (curl the videoURL) — the error text wraps the root cause, read the chained message.
  2. Verify the videoURL is fresh: re-poll CheckLipSyncStatus to get a new signed URL if the old one expired before downloading.
  3. Increase the caller's context deadline so long downloads are not cut off, and confirm ctx is not already canceled.
  4. Confirm the RateLimitedClient 'synclabs' burst/limit settings aren't causing request-drops; raise maxWait if so.
  5. Retry: callers already wrap this in withRetry (3 attempts, exponential backoff) — for persistent failures fix the network path, not the retry count.

Example fix

// before
lipVideoURL, lipErr := pollLipSync(ctx, lipCfg, lipJobID)
dlPath, lipErr := DownloadLipSyncVideo(ctx, lipCfg, lipVideoURL, "lipsync.mp4")
// after
// use a fresh context with an explicit download deadline instead of the long-lived pipeline ctx
ctxDl, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
dlPath, lipErr := DownloadLipSyncVideo(ctxDl, lipCfg, lipVideoURL, "lipsync.mp4")
Defensive patterns

Strategy: retry

Validate before calling

// before calling
u, err := url.Parse(videoURL)
if err != nil || u.Scheme != "https" || u.Host == "" {
	return fmt.Errorf("invalid video URL: %q", videoURL)
}
if ctx.Err() != nil {
	return ctx.Err() // context already canceled
}

Type guard

func isTransportError(err error) bool {
	var ne net.Error
	if errors.As(err, &ne) { return true }
	return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) || errors.Is(err, io.ErrUnexpectedEOF)
}

Try / catch

path, err := DownloadLipSyncVideo(ctx, lipCfg, lipVideoURL, "lipsync.mp4")
if err != nil {
	if isTransportError(err) {
		// transient: retry with backoff or re-fetch a fresh URL via CheckLipSyncStatus
	}
	return fmt.Errorf("download lipsync video: %w", err)
}

Prevention

When it happens

Trigger: cfg.doHTTP(req) returns a non-nil error for the GET of videoURL: network outage, unresolvable CDN hostname, the rate-limited RateLimitedClient exhausting its wait window, or ctx already canceled/expired before the download.

Common situations: Container has no outbound internet or broken DNS; Sync Labs signed video URLs have expired by the time the download happens after the 5-minute lipSyncTimeout poll; a corporate proxy/TLS MITM breaks the connection; pipeline ctx deadline expires during a long download; the rate-limited client (60s max wait) times out under burst load.

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/982dc4064216811e. Report an issue: GitHub.