Billionmail/BillionMail · error

the recipient has unsubscribed from the current group and…

Error message

the recipient has unsubscribed from the current group and cannot send

What it means

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.

Solutions

  1. Check the compose file has a top-level 'services:' mapping with correct indentation
  2. Re-validate/re-parse the YAML and inspect the resulting map before calling the rebuild
  3. Point the ConfigManager at the correct docker-compose.yml
  4. Restore the stock BillionMail docker-compose.yml

Example fix

# before
networks:
  billionmail-network: {}
# after
services:
  postfix-billionmail:
    image: billionmail/postfix
networks:
  billionmail-network: {}
Defensive patterns

Strategy: validation

Validate before calling

if svc, ok := dockerConfig["services"]; !ok || svc == nil {
	return fmt.Errorf("docker-compose.yml missing services section")
}

Type guard

func isValidComposeDoc(cfg map[string]interface{}) bool {
	_, ok := cfg["services"].(map[string]interface{})
	return ok
}

Try / catch

if err := m.rebuildPostfixServiceNetworks1(dockerConfig, configs); err != nil {
	if strings.Contains(err.Error(), "missing 'services' section") {
		// re-read / repair the file and re-parse before retrying
	}
	return err
}

Prevention

When it happens

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

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

Related errors


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

Appendix: source

Thrown at core/internal/controller/batch_mail/batch_mail_v1_api_mail_send.go:215

			"update_time": now,
		})
		if err != nil {
			return contact, err
		}
		groupId, _ := groupResult.LastInsertId()
		group.Id = int(groupId)
	} else {

		count, err := g.DB().Model("bm_contacts").
			Where("email", email).
			Where("group_id", group.Id).
			Where("active", 0).
			Count()
		if err != nil {
			return contact, fmt.Errorf("failed to check unsubscribe: %w", err)
		}
		if count > 0 {
			return contact, fmt.Errorf("the recipient has unsubscribed from the current group and cannot send")
		}
	}

	contactData := g.Map{
		"email":       email,
		"group_id":    group.Id,
		"active":      1,
		"status":      1,
		"create_time": now,
	}
	contactResult, err := g.DB().Model("bm_contacts").
		Data(contactData).
		OnConflict("email,group_id").
		Save()
	if err != nil {
		return contact, err
	}

View on GitHub (pinned to fc36c76c05)