benbjohnson/litestream · error
webdav: cannot copy to temp file: %w
Error message
webdav: cannot copy to temp file: %w
What it means
After buffering the header bytes, WriteLTXFile copies the full LTX stream (header buffer + remainder) to the temp file with io.Copy. This error wraps any read error from the source stream or write error to the temp file during that copy.
Source
Thrown at webdav/replica_client.go:270
// Stage to temporary file to get seekable reader with known size.
// This ensures compatibility with all WebDAV servers and avoids the
// unreliable chunked transfer encoding that causes silent data loss
// on common configurations (Nginx+FastCGI, Lighttpd, Apache+FastCGI).
tmpFile, err := os.CreateTemp("", "litestream-webdav-*.ltx")
if err != nil {
return nil, fmt.Errorf("webdav: cannot create temp file: %w", err)
}
defer func() {
_ = tmpFile.Close()
_ = os.Remove(tmpFile.Name())
}()
fullReader := io.MultiReader(&buf, rd)
size, err := io.Copy(tmpFile, fullReader)
if err != nil {
return nil, fmt.Errorf("webdav: cannot copy to temp file: %w", err)
}
if _, err := tmpFile.Seek(0, io.SeekStart); err != nil {
return nil, fmt.Errorf("webdav: cannot seek temp file: %w", err)
}
if err := client.MkdirAll(path.Dir(filename), 0755); err != nil {
return nil, fmt.Errorf("webdav: cannot create parent directory %q: %w", path.Dir(filename), err)
}
// Upload with Content-Length header using seekable temp file.
// WriteStreamWithLength requires both a seekable reader and known size,
// which we now have from the temp file. This avoids chunked encoding
// and ensures reliable uploads across all WebDAV server configurations.
if err := client.WriteStreamWithLength(filename, tmpFile, size, 0644); err != nil {
return nil, fmt.Errorf("webdav: cannot write file %q: %w", filename, err)
}
View on GitHub (pinned to 4ed7a308f6)
Solutions
- Check disk space on the temp partition; free space or relocate TMPDIR
- Investigate the source reader's error (the wrapped %w reveals the root cause)
- Retry the replication write; transient stream failures are typically recoverable
- Monitor for oversized LTX files exceeding available temp space
Defensive patterns
Strategy: retry
Validate before calling
if st, err := os.Stat(os.TempDir()); err != nil || st.Sys().(*syscall.Statfs_t).Bavail*uint64(st.Sys().(*syscall.Statfs_t).Bsize) < minFreeBytes { return errors.New("insufficient temp space") } Try / catch
err := replicate(ctx)
if err != nil {
if errors.Is(err, syscall.ENOSPC) { /* free space / alert */ } else if isTransient(err) { retry with backoff }
} Prevention
- Alert on low disk space before it fills
- Monitor stream-producing connections for drops
- Size temp space for the largest expected LTX file
When it happens
Trigger: The source reader (rd) errors mid-stream (underlying DB/WAL read failure, closed pipe, network reader drop) or the temp file write fails (disk full, I/O error).
Common situations: Disk filling up during a large LTX write, the database connection producing the stream dropping mid-transfer, ENOSPC on the temp partition.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- failed to scan directory %s: %w
- cannot remove tmp files: %w
- close L0 file: %w
- write page %d: %w
- sync before truncate: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/ead3b617ccde56f4.
Report an issue: GitHub.