{"record":{"id":"32f8b9832afb1679","repo":"Billionmail/BillionMail","slug":"failed-to-read-postfix-config-v","errorCode":null,"errorMessage":"failed to read postfix config: %v","messagePattern":"failed to read postfix config: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/certificate.go","lineNumber":232,"sourceCode":"\t\treturn fmt.Errorf(\"private key data is empty\")\n\t}\n\n\t// Validate certificate\n\tcInfo := acme.GetCertInfo(csrPem)\n\n\tif cInfo.Endtime == 0 {\n\t\treturn fmt.Errorf(\"certificate is invalid\")\n\t}\n\n\treturn nil\n}\n\n// updatePostfixConfig updates Postfix configuration with new certificate\nfunc (c *Certificate) updatePostfixConfig(csrPem, keyPem string) error {\n\tmainCf := public.AbsPath(consts.POSTFIX_MAIN_CONF)\n\tcontent, err := os.ReadFile(mainCf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read postfix config: %v\", err)\n\t}\n\n\t// Write certificate and key to files\n\tcertPath := public.AbsPath(filepath.Join(consts.SSL_PATH, \"postfix.crt\"))\n\tkeyPath := public.AbsPath(filepath.Join(consts.SSL_PATH, \"postfix.key\"))\n\n\tif err := os.WriteFile(certPath, []byte(csrPem), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to write certificate file: %v\", err)\n\t}\n\n\tif err := os.WriteFile(keyPath, []byte(keyPem), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to write key file: %v\", err)\n\t}\n\n\t// Update SSL certificate configuration\n\tconfig := string(content)\n\tconfig = c.updateConfigLine(config, \"smtpd_tls_key_file\", keyPath)\n\tconfig = c.updateConfigLine(config, \"smtpd_tls_cert_file\", certPath)","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/certificate.go#L214-L250","documentation":"updatePostfixConfig reads Postfix main.cf (consts.POSTFIX_MAIN_CONF) with os.ReadFile before rewriting the smtpd_tls_cert_file/key_file lines. This error wraps the read failure and is raised by SetSSL and SetPostfixSSL when main.cf is missing or unreadable.","triggerScenarios":"Calling SetSSL or SetPostfixSSL on a machine where AbsPath(consts.POSTFIX_MAIN_CONF) does not exist or cannot be read by the process user.","commonSituations":"Postfix not installed (fresh container/mail-server not initialized); custom postfix chroot/config directory; permission mismatch between the app user and /etc/postfix/main.cf.","solutions":["Verify the main.cf path (postconf -h config_directory) and correct consts.POSTFIX_MAIN_CONF or the deployment mount","Install postfix / initialize the mail server before applying certificates","Grant the process read access to main.cf","Log the wrapped error to identify whether it is ENOENT or EACCES"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"mainCf := public.AbsPath(consts.POSTFIX_MAIN_CONF)\nif _, err := os.Stat(mainCf); err != nil {\n    return fmt.Errorf(\"postfix main.cf missing at %s — install/initialize postfix first: %w\", mainCf, err)\n}","typeGuard":null,"tryCatchPattern":"if err := certService.SetSSL(csrPem, keyPem); err != nil {\n    if strings.Contains(err.Error(), \"failed to read postfix config\") {\n        log.Errorf(\"main.cf unreadable: %s\", err)\n        return ErrPostfixNotInstalled\n    }\n    return err\n}","preventionTips":["Run mail-server initialization before certificate automation","Keep consts.POSTFIX_MAIN_CONF aligned with the deployed postfix layout","Ensure the app user can read /etc/postfix/main.cf in every environment"],"tags":["postfix","configuration","file-read","tls"],"backgroundTag":"config-file-not-found","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}