Billionmail/BillionMail · error

Failed to read DKIM sign config: %v

Error message

Failed to read DKIM sign config: %v

What it means

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.

Source

Thrown at core/internal/service/domains/domains.go:613

    }
  ]
}
#%s_DKIM_END
`, domain, domain, domain, domain, domain)

			// Write DKIM sign config to file
			signConfPath := public.AbsPath(filepath.Join(consts.RSPAMD_LOCAL_D_PATH, "dkim_signing.conf"))
			signContent := `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";

domain {
#BT_DOMAIN_DKIM_BEGIN
#BT_DOMAIN_DKIM_END
}`

			if public.FileExists(signConfPath) {
				signContent, err = public.ReadFile(signConfPath)
				if err != nil {
					err = fmt.Errorf("Failed to read DKIM sign config: %v", err)
					return
				}
			}

			// Remove old config block if it exists
			pattern := fmt.Sprintf(`(?s)#%s_DKIM_BEGIN.*?#%s_DKIM_END\s*`, domain, domain)
			signContent, err = gregex.ReplaceString(pattern, "", signContent)
			if err != nil {
				return
			}

			signContent = strings.Replace(signContent, "#BT_DOMAIN_DKIM_END", signConf+"\n#BT_DOMAIN_DKIM_END", 1)
			_, err = public.WriteFile(signConfPath, signContent)
			if err != nil {
				err = fmt.Errorf("Failed to write DKIM sign config: %v", err)
				return
			}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check file permissions and ownership on the sign config path and fix with chown/chmod so the BillionMail process can read it
  2. Verify the file is a regular file, not a directory or broken symlink (ls -la on the path)
  3. Check mount options — ensure the config volume is not mounted read-only
  4. Re-run the DKIM repair (RepairDKIMSigningConfig) after fixing permissions so the config block is regenerated

Example fix

// before: file unreadable, silent failure in repair
signContent, err = public.ReadFile(signConfPath)
// after: ensure readable perms before the call
os.Chmod(signConfPath, 0o644)
signContent, err = public.ReadFile(signConfPath)
Defensive patterns

Strategy: try-catch

Validate before calling

if !public.FileExists(signConfPath) { return fmt.Errorf("sign config missing: %s", signConfPath) }
if fi, err := os.Stat(signConfPath); err == nil && !fi.Mode().IsRegular() { return fmt.Errorf("sign config not a regular file") }

Type guard

func isReadable(path string) bool {
    f, err := os.Open(path)
    if err != nil { return false }
    f.Close()
    return true
}

Try / catch

signContent, err := public.ReadFile(signConfPath)
if err != nil {
    log.Printf("DKIM sign config unreadable at %s: %v — check permissions/mounts", signConfPath, err)
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/b2e91ec67a9fe77e. Report an issue: GitHub.