siyuan-note/siyuan · error

failed to write CA private key

Error message

failed to write CA private key: %w

What it means

Error from ImportCABundle when os.WriteFile fails to persist the CA private key PEM to the TLS conf directory: disk error, missing directory, or insufficient permissions — the imported CA bundle cannot be fully installed.

Solutions

  1. Make the conf directory writable by the kernel process user (chown/chmod)
  2. If an old ca key file exists with wrong ownership, remove or chown it first
  3. Verify the filesystem is writable and has free space, then retry

Example fix

// before
// ca.key owned by root in workspace conf dir -> 0600 write denied
// after
// sudo chown <kernel-user> <workspace>/conf/tls-ca-key.pem
ImportCABundle(caCertPEM, caKeyPEM)
Defensive patterns

Strategy: try-catch

Validate before calling

info, err := os.Stat(caKeyPath); canWrite := err != nil || info.Mode().Perm()&0200 != 0

Try / catch

if err := util.ImportCABundle(caCertPEM, caKeyPEM); err != nil {
    if strings.Contains(err.Error(), "failed to write CA private key") {
        // check ownership/ACL of the existing key file in conf dir
    }
}

Prevention

When it happens

Trigger: os.WriteFile(caKeyPath, ..., 0600) fails — ConfDir missing/unwritable, read-only filesystem, disk full, or permission denied for the key path (which may have stricter ACLs than the cert).

Common situations: The kernel runs under a service account lacking write permission to the workspace conf dir; the key file exists with root-only ownership from a previous run; read-only container filesystem.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/b95699d65ae21b0d. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/cert.go:348

	keyBlock, _ := pem.Decode([]byte(caKeyPEM))
	if keyBlock == nil {
		return fmt.Errorf("failed to decode CA private key PEM")
	}

	_, err = x509.ParseECPrivateKey(keyBlock.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse CA private key: %w", err)
	}

	caCertPath := filepath.Join(ConfDir, TLSCACertFilename)
	caKeyPath := filepath.Join(ConfDir, TLSCAKeyFilename)

	if err := os.WriteFile(caCertPath, []byte(caCertPEM), 0644); err != nil {
		return fmt.Errorf("failed to write CA certificate: %w", err)
	}

	if err := os.WriteFile(caKeyPath, []byte(caKeyPEM), 0600); err != nil {
		return fmt.Errorf("failed to write CA private key: %w", err)
	}

	certPath := filepath.Join(ConfDir, TLSCertFilename)
	keyPath := filepath.Join(ConfDir, TLSKeyFilename)

	if gulu.File.IsExist(certPath) {
		os.Remove(certPath)
	}
	if gulu.File.IsExist(keyPath) {
		os.Remove(keyPath)
	}

	logging.LogInfof("imported CA bundle, server certificate will be regenerated on next TLS initialization")
	return nil
}

// trimIPv6Brackets removes brackets from IPv6 address strings like "[::1]"
func trimIPv6Brackets(ip string) string {

View on GitHub (pinned to 9f775e8a12)