kopia/kopia · error

error opening certificate file

Error message

error opening certificate file

What it means

WriteCertificateToFile wraps any error returned by os.OpenFile when it cannot open (or create/truncate) the target certificate file. The library throws it so callers writing a generated x509 certificate to disk get a single descriptive error carrying the underlying OS reason. It is a filesystem-level failure, not a certificate problem.

Solutions

  1. Create the parent directory (os.MkdirAll(filepath.Dir(fname), 0o755)) before calling WriteCertificateToFile
  2. Check and fix filesystem permissions so the process user can create the file (ls -ld on the directory)
  3. Verify fname points to a file path, not an existing directory, and that the filesystem is writable
  4. Inspect the wrapped OS error (permission denied / no such file / read-only) to target the exact cause

Example fix

// before
err := tlsutil.WriteCertificateToFile("/var/lib/app/certs/tls.crt", cert) // fails: dir missing

// after
if err := os.MkdirAll("/var/lib/app/certs", 0o755); err != nil {
	return err
}
err := tlsutil.WriteCertificateToFile("/var/lib/app/certs/tls.crt", cert)
Defensive patterns

Strategy: try-catch

Validate before calling

func canWriteCertFile(fname string) error {
	if fi, err := os.Stat(fname); err == nil && fi.IsDir() {
		return fmt.Errorf("%s is a directory", fname)
	}
	f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0o600)
	if err != nil { return err }
	f.Close()
	os.Remove(fname) // only if newly created
	return nil
}

Type guard

func isFileOpenErr(err error) bool {
	return errors.Is(err, os.ErrPermission) || errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.EISDIR)
}

Try / catch

if err := tlsutil.WriteCertificateToFile(fname, cert); err != nil {
	var pe *fs.PathError
	if errors.As(err, &pe) {
		log.Printf("cert file %s: %v", pe.Path, pe.Err)
	}
	return fmt.Errorf("writing certificate: %w", err)
}

Prevention

When it happens

Trigger: Calling WriteCertificateToFile when the directory of fname does not exist, the process lacks write permission, fname is a directory, the path is too long, or the filesystem is read-only/full.

Common situations: Configured TLS cert dir was never created before startup; running in a container with a read-only rootfs; wrong path in config (e.g. missing /tmp prefix); running as non-root user in a directory owned by root; SELinux/AppArmor blocking file creation.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/2cc927c918f75d65. Report an issue: GitHub.

Appendix: source

Thrown at internal/tlsutil/tlsutil.go:116

	}()

	privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
	if err != nil {
		return errors.Wrap(err, "Unable to marshal private key")
	}

	if err := pem.Encode(f, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
		return errors.Wrap(err, "Failed to write data to")
	}

	return nil
}

// WriteCertificateToFile writes the certificate to a given file.
func WriteCertificateToFile(fname string, cert *x509.Certificate) (err error) {
	f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, certificateFileMode) //nolint:gosec
	if err != nil {
		return errors.Wrap(err, "error opening certificate file")
	}

	defer func() {
		err = stderrors.Join(err, f.Close())
	}()

	if err := pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
		return errors.Wrap(err, "Failed to write data")
	}

	return nil
}

// TLSConfigTrustingSingleCertificate return tls.Config which trusts exactly one TLS certificate with
// provided SHA256 fingerprint.
func TLSConfigTrustingSingleCertificate(sha256Fingerprint string) *tls.Config {
	sha256FingerprintBytes, err := hex.DecodeString(sha256Fingerprint)
	if err != nil || len(sha256FingerprintBytes) < sha256.Size {

View on GitHub (pinned to 82495e54b5)