{"record":{"id":"1a020be110449031","repo":"Billionmail/BillionMail","slug":"read-dovecot-conf-failed-w","errorCode":null,"errorMessage":"read dovecot.conf failed: %w","messagePattern":"read dovecot\\.conf failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_boxes/init_quota_plugin.go","lineNumber":87,"sourceCode":"\n\t// 6. mark & reload dovecot (best-effort)\n\tif err := gfile.PutContents(markPath, fmt.Sprintf(\"First sync completed at %s\", time.Now().Format(\"2006-01-02 15:04:05\"))); err != nil {\n\t\tg.Log().Warningf(ctx, \"Failed to create the quota marker file: %v\", err)\n\t}\n\n\tif err := reloadDovecot(ctx); err != nil {\n\t\tg.Log().Warning(ctx, \"reload dovecot failed\", err)\n\t}\n\n\tg.Log().Info(ctx, \"Quota plugin initialization completed\")\n\treturn nil\n}\n\nfunc ensureDovecotConf(confRoot string) error {\n\tpath := filepath.Join(confRoot, \"dovecot.conf\")\n\tdata, err := ioutil.ReadFile(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read dovecot.conf failed: %w\", err)\n\t}\n\tcontent := string(data)\n\n\t// Search for the \"mail_plugins\" line\n\tmailPluginsRe := regexp.MustCompile(`(?m)^\\s*mail_plugins\\s*=.*$`)\n\tif mailPluginsRe.MatchString(content) {\n\t\t// If it exists but there is no quota, add a quota.\n\t\tnewContent := mailPluginsRe.ReplaceAllStringFunc(content, func(line string) string {\n\t\t\tif strings.Contains(line, \"quota\") {\n\t\t\t\treturn line\n\t\t\t}\n\t\t\treturn line + \" quota\"\n\t\t})\n\t\tcontent = newContent\n\t} else {\n\t\t// Insert before the first !include\n\t\tincludeIdx := strings.Index(content, \"!include\")\n\t\tif includeIdx > -1 {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_boxes/init_quota_plugin.go#L69-L105","documentation":"Raised in ensureDovecotConf when ioutil.ReadFile cannot load <confRoot>/dovecot.conf during quota plugin initialization. The Dovecot main config is missing at the expected path or unreadable, so the function cannot patch in the quota plugin directives; the error is wrapped with %w preserving the os error chain.","triggerScenarios":"ioutil.ReadFile(dovecot.conf) fails: file deleted or renamed, path permission denied, confRoot points to the wrong directory (often the cause of 315 being bypassed because the dir exists but the file doesn't).","commonSituations":"dovecot.conf renamed to dovecot.conf.custom by an admin; partial extraction of config files; running as a user without read permission on the conf dir; symlinked conf broken after migration.","solutions":["Confirm dovecot.conf exists at confRoot (ls the directory from error 315's path)","Fix file permissions so the process user can read it","Restore the stock dovecot.conf from the repo conf/dovecot directory","Check the wrapped error (%w) for the exact OS reason"],"exampleFix":"// before\ndata, err := ioutil.ReadFile(path)\nif err != nil {\n\treturn fmt.Errorf(\"read dovecot.conf failed: %w\", err)\n}\n// after\ndata, err := ioutil.ReadFile(path)\nif err != nil {\n\treturn fmt.Errorf(\"read dovecot.conf at %s failed: %w\", path, err)\n}","handlingStrategy":"validation","validationCode":"path := filepath.Join(confRoot, \"dovecot.conf\")\nif f, err := os.Open(path); err != nil {\n\treturn fmt.Errorf(\"dovecot.conf unreadable at %s: %v\", path, err)\n} else {\n\tf.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := mail_boxes.InitQuotaPluginAndUpdateUsedSpace(ctx); err != nil && strings.Contains(err.Error(), \"read dovecot.conf failed\") {\n\tlog.Printf(\"restore or chmod dovecot.conf: %v\", errors.Unwrap(err))\n}","preventionTips":["Never rename or delete stock dovecot.conf; use includes for customization","Ensure conf files are world-readable for the service user","Verify file presence after upgrades/migrations","Use errors.Unwrap to log the underlying OS cause"],"tags":["filesystem","dovecot","read-failure"],"backgroundTag":"config-file-read-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}