{"record":{"id":"b2e91ec67a9fe77e","repo":"Billionmail/BillionMail","slug":"failed-to-read-dkim-sign-config-v","errorCode":null,"errorMessage":"Failed to read DKIM sign config: %v","messagePattern":"Failed to read DKIM sign config: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/domains/domains.go","lineNumber":613,"sourceCode":"    }\n  ]\n}\n#%s_DKIM_END\n`, domain, domain, domain, domain, domain)\n\n\t\t\t// Write DKIM sign config to file\n\t\t\tsignConfPath := public.AbsPath(filepath.Join(consts.RSPAMD_LOCAL_D_PATH, \"dkim_signing.conf\"))\n\t\t\tsignContent := `sign_headers = \"from:sender:reply-to:subject:date:message-id:to:cc:mime-version:content-type:content-transfer-encoding:content-language:resent-to:resent-cc:resent-from:resent-sender:resent-message-id:in-reply-to:references:list-id:list-help:list-owner:list-unsubscribe:list-subscribe:list-post:list-unsubscribe-post:disposition-notification-to:disposition-notification-options:original-recipient:openpgp:autocrypt\";\n\ndomain {\n#BT_DOMAIN_DKIM_BEGIN\n#BT_DOMAIN_DKIM_END\n}`\n\n\t\t\tif public.FileExists(signConfPath) {\n\t\t\t\tsignContent, err = public.ReadFile(signConfPath)\n\t\t\t\tif err != nil {\n\t\t\t\t\terr = fmt.Errorf(\"Failed to read DKIM sign config: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove old config block if it exists\n\t\t\tpattern := fmt.Sprintf(`(?s)#%s_DKIM_BEGIN.*?#%s_DKIM_END\\s*`, domain, domain)\n\t\t\tsignContent, err = gregex.ReplaceString(pattern, \"\", signContent)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tsignContent = strings.Replace(signContent, \"#BT_DOMAIN_DKIM_END\", signConf+\"\\n#BT_DOMAIN_DKIM_END\", 1)\n\t\t\t_, err = public.WriteFile(signConfPath, signContent)\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"Failed to write DKIM sign config: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/domains/domains.go#L595-L631","documentation":"This error wraps an OS-level read failure of the rspamd DKIM signing configuration file (signConfPath, the domain-block-marked config file). getDKIMRecordWithKeySize reads this file to inject/update the domain's DKIM signing block; if public.ReadFile fails, the wrapped error is returned to GetDKIMRecord, GetDKIMShortRecord, and RepairDKIMSigningConfig callers.","triggerScenarios":"public.ReadFile(signConfPath) returns an error — file exists (public.FileExists passed) but cannot be read due to permission denial, the file being a directory or symlink target removed mid-read, disk I/O error, or a race where the file is deleted between FileExists and ReadFile.","commonSituations":"Container/host permission mismatches (rspamd config owned by _rspamd user, service runs as root or vice versa), bind-mounted config files with wrong host-side ownership, read-only filesystem, NFS stale handles, or an admin removing/replacing the config while a DKIM repair runs.","solutions":["Check file permissions and ownership on the sign config path and fix with chown/chmod so the BillionMail process can read it","Verify the file is a regular file, not a directory or broken symlink (ls -la on the path)","Check mount options — ensure the config volume is not mounted read-only","Re-run the DKIM repair (RepairDKIMSigningConfig) after fixing permissions so the config block is regenerated"],"exampleFix":"// before: file unreadable, silent failure in repair\nsignContent, err = public.ReadFile(signConfPath)\n// after: ensure readable perms before the call\nos.Chmod(signConfPath, 0o644)\nsignContent, err = public.ReadFile(signConfPath)","handlingStrategy":"try-catch","validationCode":"if !public.FileExists(signConfPath) { return fmt.Errorf(\"sign config missing: %s\", signConfPath) }\nif fi, err := os.Stat(signConfPath); err == nil && !fi.Mode().IsRegular() { return fmt.Errorf(\"sign config not a regular file\") }","typeGuard":"func isReadable(path string) bool {\n    f, err := os.Open(path)\n    if err != nil { return false }\n    f.Close()\n    return true\n}","tryCatchPattern":"signContent, err := public.ReadFile(signConfPath)\nif err != nil {\n    log.Printf(\"DKIM sign config unreadable at %s: %v — check permissions/mounts\", signConfPath, err)\n    return err\n}","preventionTips":["Pin stable ownership/permissions on rspamd config files (e.g. 0644) in deployment scripts","Keep the config volume mounted read-write and avoid read-only remounts during updates","Run RepairDKIMSigningConfig after any container/image upgrade to regenerate config blocks"],"tags":["filesystem","dkim","permissions","rspamd"],"backgroundTag":"file-read-permission-denied","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"}