siyuan-note/siyuan · critical

failed to load CA certificates: %w

Error message

failed to load CA certificates: %w

What it means

The TLS certificate manager lazily (re)issues the server leaf certificate inside refreshCertificate, first loading the local CA (caCertPath/caKeyPath) via loadCA. If reading or parsing the CA cert/key files fails for any reason, the error is wrapped with this message and GetCertificate fails, aborting the TLS handshake.

Source

Thrown at kernel/util/tls_cert_manager.go:153

	currentState := manager.state.Load()
	if currentState == nil {
		return nil, fmt.Errorf("TLS certificate is not initialized")
	}
	now := time.Now()
	if tlsCertificateStateValid(currentState, now) &&
		(localIP == nil || certificateContainsIP(currentState.leaf, localIP)) {
		return &currentState.certificate, nil
	}

	privateKey, ok := currentState.certificate.PrivateKey.(crypto.Signer)
	if !ok {
		return nil, fmt.Errorf("TLS server private key does not implement crypto.Signer")
	}

	caCert, caKey, err := loadCA(manager.caCertPath, manager.caKeyPath)
	if err != nil {
		return nil, fmt.Errorf("failed to load CA certificates: %w", err)
	}

	ipAddresses := collectServerCertificateIPs(currentState.leaf.IPAddresses, localIP)
	dnsNames := collectServerCertificateDNSNames(currentState.leaf.DNSNames)
	certDER, leaf, err := createServerCertificate(caCert, caKey, privateKey, ipAddresses, dnsNames)
	if err != nil {
		return nil, fmt.Errorf("failed to generate TLS server certificate: %w", err)
	}

	certificate := tls.Certificate{
		Certificate: [][]byte{certDER},
		PrivateKey:  privateKey,
		Leaf:        leaf,
	}
	newState := &tlsCertificateState{certificate: certificate, leaf: leaf}
	manager.state.Store(newState)

	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Check the wrapped %w cause in the log: if files are missing/corrupt, remove the stale CA files (caCertPath/caKeyPath) so a fresh CA is generated on next start
  2. Fix filesystem permissions on the workspace temp/conf directory holding the CA files
  3. Verify the CA key matches the CA certificate and both are valid unencrypted PEM
  4. If the failure persists, reinitialize the TLS cert manager state (restart kernel) to regenerate the CA

Example fix

// before: corrupted ca.key on disk -> failed to load CA certificates: ...
// after: delete stale CA pair so it is regenerated
os.Remove(manager.caCertPath)
os.Remove(manager.caKeyPath)
cert, err := manager.GetCertificate(hello) // regenerates CA + leaf
Defensive patterns

Strategy: try-catch

Validate before calling

// before handshake: verify CA files exist and parse
if _, err := os.Stat(manager.caCertPath); err != nil {
	// CA missing: regenerate or restore workspace files first
}
if _, err := tls.LoadX509KeyPair(manager.caCertPath, manager.caKeyPath); err != nil {
	// CA pair unreadable/mismatched: recreate CA before serving TLS
}

Try / catch

cert, err := certManager.GetCertificate(hello)
if err != nil {
	logging.LogErrorf("TLS handshake cert unavailable: %s", err)
	// treat connection as failed; do not serve with a nil certificate
	return nil, err
}

Prevention

When it happens

Trigger: GetCertificate triggers refreshCertificate (certificate expired, not yet valid, or new local IP not covered) and loadCA fails — CA files missing/corrupted/unreadable, wrong PEM contents, encrypted or mismatched key, or filesystem permission errors.

Common situations: Workspace data directory restored from backup without the CA files, disk/permission problems, CA key regenerated independently of the cert, or first-run CA generation silently failed earlier.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/5e21801cd9bd762e. Report an issue: GitHub.