{"record":{"id":"b95699d65ae21b0d","repo":"siyuan-note/siyuan","slug":"failed-to-write-ca-private-key-w","errorCode":null,"errorMessage":"failed to write CA private key: %w","messagePattern":"failed to write CA private key: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/cert.go","lineNumber":348,"sourceCode":"\tkeyBlock, _ := pem.Decode([]byte(caKeyPEM))\n\tif keyBlock == nil {\n\t\treturn fmt.Errorf(\"failed to decode CA private key PEM\")\n\t}\n\n\t_, err = x509.ParseECPrivateKey(keyBlock.Bytes)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse CA private key: %w\", err)\n\t}\n\n\tcaCertPath := filepath.Join(ConfDir, TLSCACertFilename)\n\tcaKeyPath := filepath.Join(ConfDir, TLSCAKeyFilename)\n\n\tif err := os.WriteFile(caCertPath, []byte(caCertPEM), 0644); err != nil {\n\t\treturn fmt.Errorf(\"failed to write CA certificate: %w\", err)\n\t}\n\n\tif err := os.WriteFile(caKeyPath, []byte(caKeyPEM), 0600); err != nil {\n\t\treturn fmt.Errorf(\"failed to write CA private key: %w\", err)\n\t}\n\n\tcertPath := filepath.Join(ConfDir, TLSCertFilename)\n\tkeyPath := filepath.Join(ConfDir, TLSKeyFilename)\n\n\tif gulu.File.IsExist(certPath) {\n\t\tos.Remove(certPath)\n\t}\n\tif gulu.File.IsExist(keyPath) {\n\t\tos.Remove(keyPath)\n\t}\n\n\tlogging.LogInfof(\"imported CA bundle, server certificate will be regenerated on next TLS initialization\")\n\treturn nil\n}\n\n// trimIPv6Brackets removes brackets from IPv6 address strings like \"[::1]\"\nfunc trimIPv6Brackets(ip string) string {","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/cert.go#L330-L366","documentation":"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.","triggerScenarios":"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.","commonSituations":"Stale ca.key with restrictive permissions from a previous run; conf dir on a nearly-full volume; permission regression after a workspace move.","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."],"exampleFix":"// before\nerr := util.ImportCABundle(certPEM, keyPEM) // -> failed to write CA private key: .../ca.key: permission denied\n\n// after\nos.Chmod(filepath.Join(util.ConfDir, util.TLSCAKeyFilename), 0600)\nerr := util.ImportCABundle(certPEM, keyPEM)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := util.ImportCABundle(certPEM, keyPEM); err != nil {\n    if strings.Contains(err.Error(), \"failed to write CA private key\") {\n        // ca.crt may already be written; fix ca.key perms then re-run both for consistency\n        keyPath := filepath.Join(util.ConfDir, util.TLSCAKeyFilename)\n        os.Chmod(keyPath, 0600) // or os.Remove(keyPath)\n        return util.ImportCABundle(certPEM, keyPEM)\n    }\n    return err\n}","preventionTips":["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."],"tags":["tls","key","filesystem","io","ca"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}