Billionmail/BillionMail · error

lipsync API call: %w

Error message

lipsync API call: %w

What it means

SubmitLipSync wraps errors from cfg.doHTTP(httpReq) — i.e. the request could not be completed at the transport level: DNS resolution failure, connection refused/TLS handshake error, timeout, or context cancellation. It is distinct from [453], which fires when the server responds with a non-2xx status; here there is no usable HTTP response at all.

Source

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

}

// SubmitLipSync submits a lip sync job and returns the job ID.
func SubmitLipSync(ctx context.Context, cfg LipSyncConfig, audioURL, videoURL string) (string, error) {
	req := LipSyncRequest{
		AudioURL:       audioURL,
		VideoURL:       videoURL,
		SynergizeAudio: true,
	}

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

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

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

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

// CheckLipSyncStatus checks the status of a lip sync job.
func CheckLipSyncStatus(ctx context.Context, cfg LipSyncConfig, jobID string) (*LipSyncResponse, error) {
	httpReq, err := BuildLipSyncStatusRequest(cfg, jobID)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Verify network connectivity/DNS to the lipsync base URL host (curl the base URL from the same machine)
  2. Check ctx deadlines and cancelations — pass a context with an adequate timeout for media upload
  3. Confirm TLS is valid (system CA bundle present in container images; correct scheme http vs https)
  4. Inspect and unwrap the wrapped error with errors.Is(err, context.DeadlineExceeded)/net.Error to classify, then retry transient kinds with backoff

Example fix

// before
id, err := SubmitLipSync(ctx, cfg, ...) // bare ctx may be canceled too early
// after
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
id, err := SubmitLipSync(ctx, cfg, ...)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) { /* retry with backoff */ }
}
Defensive patterns

Strategy: retry

Validate before calling

// before calling: cheap reachability preflight
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
if err := ctx.Err(); err != nil {
    return fmt.Errorf("context already done: %w", err)
}

Type guard

func isTransientNetErr(err error) bool {
    var ne net.Error
    if errors.As(err, &ne) && ne.Timeout() { return true }
    return errors.Is(err, context.DeadlineExceeded) ||
        errors.Is(err, syscall.ECONNREFUSED) ||
        errors.Is(err, syscall.ECONNRESET)
}

Try / catch

id, err := SubmitLipSync(ctx, cfg, ...)
if err != nil {
    if isTransientNetErr(err) {
        // retry with exponential backoff, honoring ctx
    }
    if errors.Is(err, context.Canceled) {
        // caller canceled; do not retry
    }
    return fmt.Errorf("submit failed: %w", err)
}

Prevention

When it happens

Trigger: Calling SubmitLipSync when the lipsync provider host is unreachable (DNS failure, wrong host/port), the network is down, TLS certs are invalid/expired, cfg.doHTTP has no/rejecting timeout, or the caller's ctx is canceled before the response arrives.

Common situations: Provider outage or regional block; container without outbound internet/Egress rules blocking the API; proxy required but not configured; 5-second default timeout exceeded on slow uploads; test environment pointing at a non-existent host.

Related errors


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