benbjohnson/litestream · error

webdav: cannot copy to temp file

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.

Solutions

  1. Check disk space on the temp partition; free space or relocate TMPDIR
  2. Investigate the source reader's error (the wrapped %w reveals the root cause)
  3. Retry the replication write; transient stream failures are typically recoverable
  4. 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

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


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/ead3b617ccde56f4. Report an issue: GitHub.

Appendix: 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)