kopia/kopia · error

unable to write WebDAV key

Error message

unable to write WebDAV key

What it means

After generating the TLS keypair, New() writes the private key to a temp file via tlsutil.WritePrivateKeyToFile so rclone can serve HTTPS WebDAV. Failure writing that key file is wrapped as 'unable to write WebDAV key'.

Solutions

  1. Confirm the temporary directory (td from os.MkdirTemp) still exists and is writable
  2. Check free space on the filesystem holding the temp dir
  3. Exclude kopia temp paths from aggressive tmp-cleaners
  4. Re-run the connect; a fresh temp dir is created each time
Defensive patterns

Strategy: validation

Validate before calling

td, err := os.MkdirTemp("", "kopia-rclone-probe")
if err != nil { return err }
defer os.RemoveAll(td)
probe := filepath.Join(td, "probe")
if err := os.WriteFile(probe, []byte("x"), 0o600); err != nil {
    return fmt.Errorf("temp dir not writable: %w", err)
}

Try / catch

_, err := rclone.New(ctx, opt, isCreate)
if err != nil && strings.Contains(err.Error(), "unable to write WebDAV key") {
    log.Printf("failed writing TLS key to temp dir: %v; check TMPDIR permissions/space", err)
}

Prevention

When it happens

Trigger: tlsutil.WritePrivateKeyToFile(temporaryKeyPath, key) fails — temp dir deleted mid-run, permissions problem, or disk full.

Common situations: Temp cleaner daemon removing the kopia-rclone dir concurrently; full disk on /tmp; security software blocking key material written to disk.

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 kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/4eb3c10efcd5eaee. Report an issue: GitHub.

Appendix: source

Thrown at repo/blob/rclone/rclone_storage.go:290

	// password file for rclone webdav server.
	temporaryHtpassword := filepath.Join(td, "htpasswd")

	defer func() {
		// if we return this function without setting Storage, make sure to clean everything up.
		if r.Storage == nil {
			r.Close(ctx) //nolint:errcheck
		}
	}()

	// write TLS files.
	//nolint:mnd
	cert, key, err := tlsutil.GenerateServerCertificate(ctx, 2048, 365*24*time.Hour, []string{"127.0.0.1"})
	if err != nil {
		return nil, errors.Wrap(err, "unable to generate server certificate")
	}

	if err = tlsutil.WritePrivateKeyToFile(temporaryKeyPath, key); err != nil {
		return nil, errors.Wrap(err, "unable to write WebDAV key")
	}

	if err = tlsutil.WriteCertificateToFile(temporaryCertPath, cert); err != nil {
		return nil, errors.Wrap(err, "unable to write WebDAV cert")
	}

	// temporary username and password to be used when communicating with rclone
	webdavUsername := "u" + uuid.New().String()
	webdavPassword := "p" + uuid.New().String()

	if err = htpasswd.SetPassword(temporaryHtpassword, webdavUsername, webdavPassword, htpasswd.HashAPR1); err != nil {
		return nil, errors.Wrap(err, "unable to write htpasswd file")
	}

	rcloneExe := defaultRCloneExe
	if opt.RCloneExe != "" {
		rcloneExe = opt.RCloneExe
	}

View on GitHub (pinned to 82495e54b5)