benbjohnson/litestream · error

webdav: cannot create temp file

Error message

webdav: cannot create temp file: %w

What it means

WriteLTXFile stages the incoming LTX stream to a temporary local file (to obtain a seekable reader with known size for Content-Length uploads). This error wraps the os.CreateTemp failure, meaning the local filesystem refused to create the temp file.

Solutions

  1. Check disk space on the partition holding the temp dir (df -h $TMPDIR)
  2. Verify TMPDIR is set to a writable directory, or unset it to use /tmp
  3. Fix permissions on the temp directory (writable by the litestream user)
  4. Mount a writable tmpfs/emptyDir at /tmp in the container

Example fix

// before
TMPDIR=/nonexistent litestream replicate ...  // fails
// after
mkdir -p /var/tmp/litestream && TMPDIR=/var/tmp/litestream litestream replicate ...
Defensive patterns

Strategy: validation

Validate before calling

tmpDir := os.TempDir()
if st, err := os.Stat(tmpDir); err != nil || !st.IsDir() { return fmt.Errorf("temp dir %s unavailable", tmpDir) }
probe, err := os.CreateTemp(tmpDir, "probe-*"); if err != nil { return err }; probe.Close(); os.Remove(probe.Name())

Try / catch

if err := runReplication(ctx); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, syscall.ENOSPC) { /* free disk or relocate TMPDIR */ }
}

Prevention

When it happens

Trigger: os.CreateTemp("", "litestream-webdav-*.ltx") fails: TMPDIR points to a nonexistent/unwritable directory, or the disk holding os.TempDir() is full.

Common situations: Containers with read-only /tmp, TMPDIR misconfigured in systemd units or Docker, full disk on the litestream host.

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/b0bba210f3d824ae. Report an issue: GitHub.

Appendix: source

Thrown at webdav/replica_client.go:259

	filename := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)

	var buf bytes.Buffer
	teeReader := io.TeeReader(rd, &buf)

	hdr, _, err := ltx.PeekHeader(teeReader)
	if err != nil {
		return nil, fmt.Errorf("extract timestamp from LTX header: %w", err)
	}
	timestamp := time.UnixMilli(hdr.Timestamp).UTC()

	// 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 {

View on GitHub (pinned to 4ed7a308f6)