Billionmail/BillionMail · error
download error %d
Error message
download error %d
What it means
After the GET succeeds, this fires when the video-hosting server replies with any status other than 200 OK. Unlike the submit/status calls, this branch does NOT read the response body, so the server's error detail is lost — you only get the numeric code. Common codes: 403 (signed URL expired/forbidden), 404 (video removed), 5xx (CDN/storage errors).
Source
Thrown at core/internal/service/video_gen/lipsync.go:179
// 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)
}
return outPath, nil
}
View on GitHub (pinned to fc36c76c05)
Solutions
- Log the actual status code (and ideally body) — 403/404 almost always mean the URL expired; re-run CheckLipSyncStatus to obtain a fresh video_url and download immediately after 'completed'.
- Do not poll long then download late: fetch a new URL right before downloading instead of caching lipVideoURL.
- Verify videoURL is the full https URL from resp.VideoURL, not a relative or truncated value.
- If 5xx, retry — the pipeline already wraps this in withRetry; persistent 5xx means wait for the provider to recover.
- Inspect any proxy/firewall in front of the host if you get unexpected 403s from your own network.
Example fix
// before
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("download error %d", resp.StatusCode)
}
// after
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
return "", fmt.Errorf("download error %d: %s", resp.StatusCode, string(body))
} Defensive patterns
Strategy: retry
Validate before calling
// verify URL freshness right before downloading
st, err := CheckLipSyncStatus(ctx, lipCfg, lipJobID)
if err != nil { return err }
if st.Status != "completed" || st.VideoURL == "" {
return fmt.Errorf("lip sync not ready: status=%s", st.Status)
}
videoURL = st.VideoURL // use a freshly issued URL Type guard
func isRetryableStatus(code int) bool {
return code == http.StatusTooManyRequests || code >= 500
} Try / catch
path, err := DownloadLipSyncVideo(ctx, lipCfg, videoURL, "lipsync.mp4")
var herr interface{ ... } // or check message
if err != nil && strings.Contains(err.Error(), "download error 40") {
// 403/404: URL expired or bad — re-fetch status, get new URL, retry once
} else if err != nil && strings.Contains(err.Error(), "download error 5") {
// transient server error — retry with backoff
} Prevention
- Never cache videoURL across long delays; fetch a fresh one from CheckLipSyncStatus right before download
- Treat 403/404 as 'refresh URL and retry', 5xx/429 as 'backoff and retry'
- Instrument/log the status code so expired-vs-forbidden is distinguishable
- Check Sync Labs status page if 5xx persists across retries
When it happens
Trigger: resp.StatusCode != http.StatusOK on the GET of the completed lip-sync video URL — typically calling DownloadLipSyncVideo with a stale or wrong videoURL, or Sync Labs' storage returning 403/404/500.
Common situations: Downloading hours after the lip-sync job finished and the signed CDN URL expired; passing an empty or placeholder videoURL (empty string would fail earlier, but a malformed URL hits 404); Sync Labs outage returning 503; firewall/proxy returning a 403 block page.
Related errors
- create lipsync request: %w
- create status request: %w
- lipsync API error %d: %s
- lipsync status API error %d: %s
- download lipsync video: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/58c63f428d188ba2.
Report an issue: GitHub.