Billionmail/BillionMail · error
write video data: %w
Error message
write video data: %w
What it means
io.Copy(f, resp.Body) failed while streaming the video bytes to disk. This means the transfer was interrupted mid-download — connection reset/timeout by the CDN or rate-limited client, or a local write error (disk full). A partial/truncated lipsync.mp4 may be left on disk since the file is not cleaned up on failure.
Source
Thrown at core/internal/service/video_gen/lipsync.go:190
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
- Check disk space first — a local ENOSPC surfaces here just like a network error.
- Wrap the copy in an explicit context/timeout sized for the file size, or remove the tight client timeout for downloads.
- Delete the partial file on error so stale truncated videos are never uploaded: os.Remove(outPath) before returning.
- Compare downloaded size with Content-Length to detect truncation; retry the whole download if mismatched.
- Use a buffered copy with periodic flush or resumable Range requests for very large files over flaky networks.
Example fix
// before
if _, err := io.Copy(f, resp.Body); err != nil {
return "", fmt.Errorf("write video data: %w", err)
}
// after
if _, err := io.Copy(f, resp.Body); err != nil {
f.Close()
os.Remove(outPath) // don't leave a truncated video on disk
return "", fmt.Errorf("write video data: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
// pre-flight checks before download
if free, err := freeDisk(cfg.OutputDir); err == nil && free < minRequiredBytes {
return fmt.Errorf("insufficient disk: %d bytes free", free)
}
req := http.NewRequestWithContext(ctx, "HEAD", videoURL, nil) // optional: check size via Content-Length Type guard
func isInterruptedCopy(err error) bool {
return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) ||
errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.EIO) || errors.Is(err, syscall.ENOSPC)
} Try / catch
path, err := DownloadLipSyncVideo(ctx, cfg, videoURL, filename)
if err != nil && strings.Contains(err.Error(), "write video data") {
os.Remove(filepath.Join(cfg.OutputDir, filename)) // remove partial file
if isInterruptedCopy(err) { /* retry download with fresh context */ }
return err
} Prevention
- Always delete the partial output file on failure so truncated videos never reach upload
- Size the context/client timeout to the expected file size, not a fixed short window
- Alert on disk usage in the output/tmp volumes
- Verify downloaded size against Content-Length to detect silent truncation
When it happens
Trigger: Reading from resp.Body errors (connection reset, context deadline exceeded during a large download, rate-limited client cutting the connection) or writing to f errors (ENOSPC, I/O error on the mount).
Common situations: Large videos exceeding a proxy/LB idle timeout; context canceled mid-stream when the pipeline deadline hits; disk filling up during the copy; RateLimitedClient's 60s wait/timeout budget too small for multi-hundred-MB downloads.
Related errors
- download file failed:
- download lipsync video: %w
- write audio data: %w
- base URL not configured
- failed to write content to zip for %s: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/610126f35a73add3.
Report an issue: GitHub.