{"record":{"id":"37af508609e0b7f4","repo":"Billionmail/BillionMail","slug":"alert-settings-file-is-empty","errorCode":null,"errorMessage":"alert settings file is empty","messagePattern":"alert settings file is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/internal/service/domains/blacklist.go","lineNumber":462,"sourceCode":"type BlacklistAlertSettings struct {\n\tName          string   `json:\"name\"`\n\tSenderEmail   string   `json:\"sender_email\"`\n\tSMTPPassword  string   `json:\"smtp_password\"`\n\tSMTPServer    string   `json:\"smtp_server\"`\n\tSMTPPort      int      `json:\"smtp_port\"`\n\tRecipientList []string `json:\"recipient_list\"`\n}\n\nfunc loadBlacklistAlertSettingsForAlert() (*BlacklistAlertSettings, error) {\n\talertSettingsFile := public.AbsPath(\"../core/data/blacklist_alert_settings.json\")\n\n\tif !gfile.Exists(alertSettingsFile) {\n\t\treturn nil, fmt.Errorf(\"alert settings file not found\")\n\t}\n\n\tcontent := gfile.GetContents(alertSettingsFile)\n\tif content == \"\" {\n\t\treturn nil, fmt.Errorf(\"alert settings file is empty\")\n\t}\n\n\tvar settings BlacklistAlertSettings\n\terr := json.Unmarshal([]byte(content), &settings)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse alert settings: %v\", err)\n\t}\n\n\treturn &settings, nil\n}\n\nfunc sendAlertEmail(ctx context.Context, settings *BlacklistAlertSettings, subject, body string) error {\n\n\tsender := mail_service.NewEmailSender()\n\tsender.Host = settings.SMTPServer\n\tsender.Port = fmt.Sprintf(\"%d\", settings.SMTPPort)\n\tsender.Email = settings.SenderEmail\n\tsender.UserName = settings.SenderEmail","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/domains/blacklist.go#L444-L480","documentation":"After confirming blacklist_alert_settings.json exists, loadBlacklistAlertSettingsForAlert reads it with gfile.GetContents and returns 'alert settings file is empty' when the content is an empty string. This protects json.Unmarshal from a confusing 'unexpected end of JSON input' error by reporting the actual problem.","triggerScenarios":"sendBlacklistAlert runs while ../core/data/blacklist_alert_settings.json exists but contains zero bytes — e.g. a truncated write, an editor/UI that created the file without content, or a volume mount failure yielding an empty placeholder file.","commonSituations":"Settings saved to the file failed mid-write (disk full, container killed); a user created the file manually but left it empty; an empty bind-mounted file shadows the real one; crash during initial settings serialization.","solutions":["Re-save the blacklist alert settings through the admin UI to rewrite the file with valid JSON","Validate the file content: cat ../core/data/blacklist_alert_settings.json and restore a backup if empty","Ensure settings writes are atomic (write temp file + rename) to avoid truncation","Check disk space and volume mount health on the host"],"exampleFix":"// before\ncontent := gfile.GetContents(alertSettingsFile)\nif content == \"\" {\n\treturn nil, fmt.Errorf(\"alert settings file is empty\")\n}\n// after\ncontent := strings.TrimSpace(gfile.GetContents(alertSettingsFile))\nif content == \"\" {\n\treturn nil, fmt.Errorf(\"alert settings file %s is empty; re-save alert settings in the admin UI\", alertSettingsFile)\n}","handlingStrategy":"validation","validationCode":"content := gfile.GetContents(public.AbsPath(\"../core/data/blacklist_alert_settings.json\"))\nif strings.TrimSpace(content) == \"\" {\n\treturn fmt.Errorf(\"alert settings file is empty; re-save settings in the admin UI\")\n}\nif !json.Valid([]byte(content)) {\n\treturn fmt.Errorf(\"alert settings file is not valid JSON\")\n}","typeGuard":null,"tryCatchPattern":"settings, err := loadBlacklistAlertSettingsForAlert()\nif err != nil && strings.Contains(err.Error(), \"alert settings file is empty\") {\n\t// regenerate defaults or notify admin to re-save settings\n}","preventionTips":["Always save settings through the UI/API rather than hand-editing the file","Use atomic writes (temp file + rename) when persisting settings","Monitor the settings file size/content after upgrades or restores","Restore from backup if the file is found empty on boot"],"tags":["configuration","filesystem","json"],"backgroundTag":"empty-config-file","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"}