Billionmail/BillionMail · error

lipsync status API call: %w

Error message

lipsync status API call: %w

What it means

CheckLipSyncStatus polls the Sync Labs lipsync API (GET {base}/lipsync/{jobID}) to fetch a job's status. This error wraps a transport-level failure returned by the configured HTTP client (RateLimitedClient or http.DefaultClient) before any status code can be inspected. It means the request never completed successfully at the HTTP level — DNS failure, connection refused/reset, TLS error, timeout, or context cancellation.

Source

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

	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)
	if err != nil {
		return nil, err
	}
	httpReq = httpReq.WithContext(ctx)

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

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

	var result LipSyncResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		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 {

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Verify network connectivity to the lipsync API host (curl the base URL) and fix DNS/proxy/firewall issues.
  2. If BaseURL is overridden for testing, confirm the mock server is running and the URL scheme/host/port are correct.
  3. Check whether the passed ctx was canceled or expired; give polling a context with an adequate deadline.
  4. Inspect the wrapped cause (%w) with errors.Is/As for net.Error, context.DeadlineExceeded, or rate-limiter exhaustion and apply the matching fix (retry with backoff, longer timeout).

Example fix

// before: pollLipSync uses a bare ctx that may expire mid-poll
status, err := video_gen.CheckLipSyncStatus(ctx, cfg, jobID)
// after: bound polling with an explicit deadline and classify the error
pollCtx, cancel := context.WithTimeout(ctx, 10*time.Minute)
defer cancel()
status, err := video_gen.CheckLipSyncStatus(pollCtx, cfg, jobID)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || isNetError(err) {
        // retry with backoff
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight reachability check
if cfg.BaseURL != "" {
    u, err := url.Parse(cfg.BaseURL)
    if err != nil || u.Host == "" { return fmt.Errorf("bad lipsync BaseURL: %w", err) }
}
if err := ctx.Err(); err != nil { return err } // context already dead

Type guard

func isNetError(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, syscall.ECONNREFUSED)
}

Try / catch

status, err := video_gen.CheckLipSyncStatus(ctx, cfg, jobID)
if err != nil {
    if isNetError(err) {
        // transient: retry with exponential backoff up to N times
    }
    return fmt.Errorf("poll lipsync job %s: %w", jobID, err)
}

Prevention

When it happens

Trigger: Calling CheckLipSyncStatus(ctx, cfg, jobID) when cfg.doHTTP returns a non-nil error: network outage, unreachable api.synclabs.so, misconfigured cfg.BaseURL (e.g. wrong port or scheme for a test stub), an expired/canceled ctx, or the RateLimitedClient giving up after exhausting retries.

Common situations: No internet/DNS in the container; SYNCLABS_API_KEY environment points at a mocked BaseURL that is not running; job polling outlives a short-lived HTTP request context; rate-limited client aborts during a traffic spike.

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