Billionmail/BillionMail · error
failed to get recipient count: %w
Error message
failed to get recipient count: %w
What it means
WarmupCampaignService.CalculateEstimatedTime counts unsent recipients with SELECT COUNT(*) on recipient_info WHERE task_id = ? AND is_sent = 0. If the count query fails — DB unreachable, bad credentials, missing table/column, or cancelled context — it returns -1 and wraps the cause as 'failed to get recipient count'. Called from AssociateCampaignWithWarmup, this blocks associating a campaign with a warmup task.
Source
Thrown at core/internal/service/warmup/warmup_with_campaigns.go:110
g.Log().Errorf(ctx, "AssociateCampaignWithWarmup: failed to create association for task ID %d and warmup ID %d: %v", taskId, warmupId, err)
return nil, err
}
result := &CampaignWarmupAssociation{
CampaignWarmup: *newLink,
EstimatedSeconds: estimatedSeconds,
}
g.Log().Infof(ctx, "Successfully associated task ID %d with warmup ID %d (IP: %s). Estimated time: %d seconds.", taskId, warmupId, senderIp, estimatedSeconds)
return result, nil
}
// CalculateEstimatedTime calculates the estimated sending time in seconds for a given task and IP.
func (s *WarmupCampaignService) CalculateEstimatedTime(ctx context.Context, taskId int64, senderIp string) (int64, error) {
// Get the total number of unsent recipients
unsentCount, err := g.DB().Model("recipient_info").Ctx(ctx).Where("task_id", taskId).Where("is_sent", 0).Count()
if err != nil {
return -1, fmt.Errorf("failed to get recipient count: %w", err)
}
if unsentCount == 0 {
return 0, nil
}
// Calculate the total hourly sending rate
totalHourlyRate := 0
providerGroups := []string{
consts.MailProviderGroupGmail,
consts.MailProviderGroupYahoo,
consts.MailProviderGroupOutlook,
consts.MailProviderGroupApple,
consts.MailProviderGroupProton,
consts.MailProviderGroupZoho,
consts.MailProviderGroupAmazon,
consts.MailProviderGroupOther,
}View on GitHub (pinned to fc36c76c05)
Solutions
- Inspect the wrapped error for the concrete DB cause (connection vs schema).
- Verify recipient_info exists with task_id and is_sent columns; run migrations.
- Test DB connectivity/credentials for the environment's config.
- Check DB load/pool settings if it fails only under load.
- Retry transient failures with backoff before failing campaign association.
Example fix
// before
unsentCount, err := g.DB().Model("recipient_info").Ctx(ctx).Where("task_id", taskId).Where("is_sent", 0).Count()
if err != nil {
return -1, fmt.Errorf("failed to get recipient count: %w", err)
}
// after
unsentCount, err := g.DB().Model("recipient_info").Ctx(ctx).
Where("task_id", taskId).Where("is_sent", 0).Count()
if err != nil {
return -1, fmt.Errorf("failed to get recipient count for task %d: %w", taskId, err)
} Defensive patterns
Strategy: retry
Validate before calling
// verify warmup schema before associating a campaign
if _, err := g.DB().Ctx(ctx).Model("recipient_info").Limit(1).One(); err != nil {
return fmt.Errorf("recipient_info table unavailable: %w", err)
}
if err := g.DB().Ctx(ctx).Exec("SELECT 1"); err != nil {
return fmt.Errorf("database unavailable: %w", err)
} Try / catch
secs, err := svc.CalculateEstimatedTime(ctx, taskId, senderIp)
if err != nil {
if strings.Contains(err.Error(), "failed to get recipient count") {
// check ctx and DB health, retry transient failures
if ctx.Err() != nil {
return fmt.Errorf("cancelled: %w", ctx.Err())
}
}
return err
}
if secs < 0 {
return errors.New("unexpected negative estimate")
} Prevention
- Ensure recipient_info migrations run before warmup features start
- Validate taskId exists before calculating estimates
- Watch for -1 sentinel returns and treat them as failures
- Add retry/backoff around count queries during DB load spikes
When it happens
Trigger: recipient_info table missing or renamed, DB connection failure/timeout, context cancelled during the count, task_id column absent after schema drift.
Common situations: Deployments with un-run migrations, transient DB outages during warmup scheduling, pool exhaustion under load, wrong environment config pointing at a DB without the warmup schema.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- getSenderIdentitiesForIp err: %v
- failed to get contacts trend: %w
- failed to query relay domain mappings: %v
- failed to get all domains: %w
- fail to check domain: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/dbf61894cffc841b.
Report an issue: GitHub.