siyuan-note/siyuan · error
failed to write CA private key: %w
Error message
failed to write CA private key: %w
What it means
Returned by ImportCABundle when os.WriteFile fails writing caKeyPEM to <ConfDir>/ca.key (mode 0600). The CA cert has already been written by this point, so a failure here leaves ca.crt on disk without a matching ca.key — the import is partially applied and the kernel's TLS init will not be able to sign server certs until resolved.
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 251596fc0d)
Solutions
- Remove or fix permissions on any existing <ConfDir>/ca.key (must be writable by the kernel user, ideally 0600).
- Ensure ConfDir is writable and has free space before importing.
- If ca.crt was written but ca.key failed, re-run ImportCABundle with both PEMs after fixing the FS issue, so the pair is consistent.
Example fix
// before err := util.ImportCABundle(certPEM, keyPEM) // -> failed to write CA private key: .../ca.key: permission denied // after os.Chmod(filepath.Join(util.ConfDir, util.TLSCAKeyFilename), 0600) err := util.ImportCABundle(certPEM, keyPEM)
Defensive patterns
Strategy: try-catch
Try / catch
if err := util.ImportCABundle(certPEM, keyPEM); err != nil {
if strings.Contains(err.Error(), "failed to write CA private key") {
// ca.crt may already be written; fix ca.key perms then re-run both for consistency
keyPath := filepath.Join(util.ConfDir, util.TLSCAKeyFilename)
os.Chmod(keyPath, 0600) // or os.Remove(keyPath)
return util.ImportCABundle(certPEM, keyPEM)
}
return err
} Prevention
- Pre-clear or fix permissions on an existing ca.key before importing.
- Re-run ImportCABundle with both PEMs after a partial write so the cert/key pair stays consistent.
- Ensure the conf directory is writable and has free space before importing.
When it happens
Trigger: Calling ImportCABundle where writing ca.key fails specifically — e.g. an existing ca.key owned by another user, read-only conf, or ENOSPC after ca.crt was written.
Common situations: Stale ca.key with restrictive permissions from a previous run; conf dir on a nearly-full volume; permission regression after a workspace move.
Related errors
- failed to write CA certificate: %w
- failed to decode CA private key PEM
- failed to parse CA private key: %w
- write data [%s] failed: %s
- read image failed: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/b95699d65ae21b0d.
Report an issue: GitHub.