Billionmail/BillionMail · error
Failed to save private key file
Error message
Failed to save private key file: {} What it means
Thrown in ApplySSLWithExistingServer when public.WriteFile fails to persist private_key.pem into savePath, after certificate.pem was written successfully. It wraps the raw OS error so the actual cause (permissions, disk full, path issues) is in the message.
Solutions
- Read the embedded OS error and fix the filesystem condition (permissions, disk space)
- Verify the process can create files in savePath (touch a test file as the service user)
- Free disk space / raise quota if 'no space left on device' is reported
- Ensure nothing removes or chmods savePath between the certificate and key writes
Example fix
// before
// cert dir writable only by root; service runs as mailuser
_, _, err = svc.ApplySSLWithExistingServer(ctx, d, keyType, c, k, "/etc/ssl/private")
// after
os.Chown("/etc/ssl/private/mailuser", uid, gid) // or use a dedicated dir owned by the service user
_, _, err = svc.ApplySSLWithExistingServer(ctx, d, keyType, c, k, "/etc/ssl/private/mailuser") Defensive patterns
Strategy: validation
Validate before calling
if err := os.MkdirAll(savePath, 0750); err != nil { return err }
if err := unix.Access(savePath, unix.W_OK); err != nil { return fmt.Errorf("%s not writable: %w", savePath, err) }
// ensure free space for both files
if st, err := os.Statfs(savePath); err == nil && st.Bavail*uint64(st.Bsize) < 64*1024 { return errors.New("insufficient space for key material") } Try / catch
if _, _, err := svc.ApplySSLWithExistingServer(ctx, d, kt, c, k, savePath); err != nil {
if strings.Contains(err.Error(), "private key") {
log.Printf("key persistence failed for %s: %v", savePath, err)
// cert.pem may exist; clean up partial state before retry
os.Remove(filepath.Join(savePath, "certificate.pem"))
}
return err
} Prevention
- Verify writability of savePath as the service user before issuance
- Monitor disk usage; key writes fail on full volumes
- Avoid concurrent jobs chmod/chown the directory mid-write
- Use restrictive-but-owned dirs (0750, service user) for key material
When it happens
Trigger: ApplySSLWithExistingServer calls public.WriteFile(privateKeyFile, string(certificates.PrivateKey)) and the OS write fails — typically same causes as the certificate write: permissions, full disk, directory removed mid-operation.
Common situations: Save directory ownership changed between writes by an external sync; quota exceeded mid-write; disk filled up by the preceding certificate write in a constrained container.
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
- Failed to save certificate file
- failed to write key file
- Failed to create directory
- failed to save certificate
- failed to save private key
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/da17d851fa9ae9b1.
Report an issue: GitHub.
Appendix: source
Thrown at core/internal/service/acme/acme.go:489
if !public.FileExists(savePath) {
err = os.MkdirAll(savePath, 0750)
if err != nil {
return "", "", errors.New(public.LangCtx(ctx, "Failed to create directory: {}", err.Error()))
}
}
// Save certificate and private key files
certificateFile := filepath.Join(savePath, "certificate.pem")
privateKeyFile := filepath.Join(savePath, "private_key.pem")
_, err = public.WriteFile(certificateFile, string(certificates.Certificate))
if err != nil {
return "", "", errors.New(public.LangCtx(ctx, "Failed to save certificate file: {}", err.Error()))
}
_, err = public.WriteFile(privateKeyFile, string(certificates.PrivateKey))
if err != nil {
return "", "", errors.New(public.LangCtx(ctx, "Failed to save private key file: {}", err.Error()))
}
}
// Return certificate
return string(certificates.Certificate), string(certificates.PrivateKey), nil
}
type CertInfo v1.CertInfo
/**
* @description: Get certificate information
* @param {string} certificateStr Certificate string
* @return {CertInfo} Certificate information
*/
func GetCertInfo(certificateStr string) CertInfo {
certInfo := CertInfo{}
block, _ := pem.Decode([]byte(certificateStr))
if block == nil {View on GitHub (pinned to fc36c76c05)