{"record":{"id":"e27868714a62739c","repo":"Billionmail/BillionMail","slug":"failed-to-read-dovecot-config-v","errorCode":null,"errorMessage":"failed to read dovecot config: %v","messagePattern":"failed to read dovecot config: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/certificate.go","lineNumber":353,"sourceCode":"\n\tif err := os.WriteFile(c.PostfixSNIPath, []byte(strings.Join(lines, \"\\n\")), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to write postfix config: %v\", err)\n\t}\n\n\t// Rehash configuration\n\tif _, err := c.dockerApiClient().ExecCommandByName(context.Background(), consts.SERVICES.Postfix, []string{\"postmap\", \"/etc/postfix/conf/vmail_ssl.map\"}, \"root\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to hash postfix config: %v\", err)\n\t}\n\n\treturn nil\n}\n\n// updateDovecotConfig updates Dovecot configuration with new certificate\nfunc (c *Certificate) updateDovecotConfig(csrPem, keyPem string) error {\n\tdovecotConf := c.DovecotSslConf\n\tcontent, err := os.ReadFile(dovecotConf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read dovecot config: %v\", err)\n\t}\n\n\t// Write certificate and key to files\n\tcertPath := public.AbsPath(filepath.Join(consts.SSL_PATH, \"dovecot.crt\"))\n\tkeyPath := public.AbsPath(filepath.Join(consts.SSL_PATH, \"dovecot.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, \"ssl_cert\", \"<\"+certPath)\n\tconfig = c.updateConfigLine(config, \"ssl_key\", \"<\"+keyPath)","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/certificate.go#L335-L371","documentation":"updateDovecotConfig reads the Dovecot SSL config file (c.DovecotSslConf) with os.ReadFile before rewriting ssl_cert/ssl_key lines. When that read fails (missing file, bad path, permission denied), the underlying OS error is wrapped as 'failed to read dovecot config: %v'. It is thrown by SetSSL and SetDovecotSSL when applying a new certificate to the mail server.","triggerScenarios":"Calling SetSSL or SetDovecotSSL when the Dovecot ssl conf path (c.DovecotSslConf) does not exist, points to a directory, or the process lacks read permission on it.","commonSituations":"Running inside a container where /etc/dovecot/conf.d/ssl.conf was never mounted; Docker volume remapped after an upgrade; wrong DOVECOT conf path constant after reorganization; running BillionMail as non-root without read access.","solutions":["Verify the file at c.DovecotSslConf exists (ls -l) and is a regular readable file","Fix the DOVECOT ssl conf path constant / volume mount so the file is present inside the container","Run the process with sufficient permissions (root or the file's owning group)","Reinstall/restore the default dovecot ssl conf from the image or conf/ directory"],"exampleFix":"// before\ndovecotConf := c.DovecotSslConf\ncontent, err := os.ReadFile(dovecotConf)\n// after\ndovecotConf := c.DovecotSslConf\nif _, statErr := os.Stat(dovecotConf); statErr != nil {\n    return fmt.Errorf(\"dovecot config %s missing: %w\", dovecotConf, statErr)\n}\ncontent, err := os.ReadFile(dovecotConf)","handlingStrategy":"validation","validationCode":"if info, err := os.Stat(c.DovecotSslConf); err != nil || info.IsDir() {\n    return fmt.Errorf(\"dovecot ssl conf not readable at %s\", c.DovecotSslConf)\n}","typeGuard":null,"tryCatchPattern":"if err := certSvc.SetSSL(ctx, domain); err != nil {\n    if strings.Contains(err.Error(), \"failed to read dovecot config\") {\n        // check mount/permissions on the conf path before retrying\n    }\n    return err\n}","preventionTips":["Verify the dovecot ssl conf path is mounted into the container before calling SetSSL","Run the service with the same user that owns the dovecot config files","Keep conf paths centralized in consts so upgrades don't silently change them"],"tags":["filesystem","dovecot","file-io","config"],"backgroundTag":"file-not-found","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}