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

  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.