{"record":{"id":"6397c677365fc8e6","repo":"Billionmail/BillionMail","slug":"the-recipient-has-unsubscribed-from-the-current-gr","errorCode":null,"errorMessage":"the recipient has unsubscribed from the current group and cannot send","messagePattern":"the recipient has unsubscribed from the current group and cannot send","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/batch_mail/batch_mail_v1_api_mail_send.go","lineNumber":215,"sourceCode":"\t\t\t\"update_time\": now,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn contact, err\n\t\t}\n\t\tgroupId, _ := groupResult.LastInsertId()\n\t\tgroup.Id = int(groupId)\n\t} else {\n\n\t\tcount, err := g.DB().Model(\"bm_contacts\").\n\t\t\tWhere(\"email\", email).\n\t\t\tWhere(\"group_id\", group.Id).\n\t\t\tWhere(\"active\", 0).\n\t\t\tCount()\n\t\tif err != nil {\n\t\t\treturn contact, fmt.Errorf(\"failed to check unsubscribe: %w\", err)\n\t\t}\n\t\tif count > 0 {\n\t\t\treturn contact, fmt.Errorf(\"the recipient has unsubscribed from the current group and cannot send\")\n\t\t}\n\t}\n\n\tcontactData := g.Map{\n\t\t\"email\":       email,\n\t\t\"group_id\":    group.Id,\n\t\t\"active\":      1,\n\t\t\"status\":      1,\n\t\t\"create_time\": now,\n\t}\n\tcontactResult, err := g.DB().Model(\"bm_contacts\").\n\t\tData(contactData).\n\t\tOnConflict(\"email,group_id\").\n\t\tSave()\n\tif err != nil {\n\t\treturn contact, err\n\t}\n","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/batch_mail/batch_mail_v1_api_mail_send.go#L197-L233","documentation":"rebuildPostfixServiceNetworks1 (the second, type-checked rebuild variant) requires the parsed docker-compose config to expose a top-level 'services' mapping. It throws this error when dockerConfig lacks the key or it is not map[string]interface{}, indicating the file is not a recognizable docker-compose document.","triggerScenarios":"Calling rebuildPostfixServiceNetworks1 with a config from a YAML file missing 'services:', an empty file, or YAML where 'services' decodes to a non-map value (nil, array).","commonSituations":"Corrupted or truncated docker-compose.yml after a failed edit; passing a partial config; pointing the manager at the wrong YAML file (e.g. an env file).","solutions":["Check the compose file has a top-level 'services:' mapping with correct indentation","Re-validate/re-parse the YAML and inspect the resulting map before calling the rebuild","Point the ConfigManager at the correct docker-compose.yml","Restore the stock BillionMail docker-compose.yml"],"exampleFix":"# before\nnetworks:\n  billionmail-network: {}\n# after\nservices:\n  postfix-billionmail:\n    image: billionmail/postfix\nnetworks:\n  billionmail-network: {}","handlingStrategy":"validation","validationCode":"if svc, ok := dockerConfig[\"services\"]; !ok || svc == nil {\n\treturn fmt.Errorf(\"docker-compose.yml missing services section\")\n}","typeGuard":"func isValidComposeDoc(cfg map[string]interface{}) bool {\n\t_, ok := cfg[\"services\"].(map[string]interface{})\n\treturn ok\n}","tryCatchPattern":"if err := m.rebuildPostfixServiceNetworks1(dockerConfig, configs); err != nil {\n\tif strings.Contains(err.Error(), \"missing 'services' section\") {\n\t\t// re-read / repair the file and re-parse before retrying\n\t}\n\treturn err\n}","preventionTips":["Validate YAML parses to a map with a 'services' key before editing","Protect the compose file from partial writes (atomic rename)","Keep backups of docker-compose.yml so a corrupted file can be restored quickly","Point ConfigManager at the verified main compose file path"],"tags":["docker-compose","multi-ip","yaml","type-assertion"],"backgroundTag":"missing-compose-services-key","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}