Billionmail/BillionMail · error

failed to load API templates: %v

Error message

failed to load API templates: %v

What it means

preloadData bulk-loads api_templates rows for the API IDs referenced by the queued mails. If the PostgreSQL query fails (connection issue, permission, bad schema, driver error), the underlying DB error is wrapped with fmt.Errorf as "failed to load API templates: %v". This is a database read failure, not a 'template missing' error — a missing ID simply yields an empty result set.

Source

Thrown at core/internal/service/batch_mail/api_mail_send.go:372

	return nil
}

func preloadData(ctx context.Context, logs []ApiMailLog) (*CacheData, error) {
	// Collect all necessary IDs
	apiIds := make([]int, 0, len(logs))
	recipientEmails := make([]string, 0, len(logs))
	for _, log := range logs {
		apiIds = append(apiIds, log.ApiId)
		recipientEmails = append(recipientEmails, log.Recipient)
	}
	// Batch query API templates
	var apiTemplates []entity.ApiTemplates
	err := g.DB().Model("api_templates").
		WhereIn("id", apiIds).
		Ctx(ctx).
		Scan(&apiTemplates)
	if err != nil {
		return nil, fmt.Errorf("failed to load API templates: %v", err)
	}
	// Collect template IDs
	templateIds := make([]int, 0, len(apiTemplates))
	for _, t := range apiTemplates {
		templateIds = append(templateIds, t.TemplateId)
	}
	// Batch query email templates
	var emailTemplates []entity.EmailTemplate
	err = g.DB().Model("email_templates").
		WhereIn("id", templateIds).
		Ctx(ctx).
		Scan(&emailTemplates)
	if err != nil {
		return nil, fmt.Errorf("failed to load email templates: %v", err)
	}
	// Batch query contacts
	var contacts []entity.Contact
	err = g.DB().Model("bm_contacts").

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check PostgreSQL availability and app logs for the underlying wrapped DB error
  2. Confirm the api_templates table exists with the expected schema (run pending migrations)
  3. Verify DB credentials/permissions grant SELECT on api_templates
  4. Retry the queue processing once the DB connection is healthy

Example fix

// before: hard fail whole batch on transient DB error
return nil, fmt.Errorf("failed to load API templates: %v", err)
// after: retry transient failures with backoff
var apiTemplates []entity.ApiTemplates
for attempt := 0; attempt < 3; attempt++ {
    err = g.DB().Model("api_templates").WhereIn("id", apiIds).Ctx(ctx).Scan(&apiTemplates)
    if err == nil { break }
    time.Sleep(time.Duration(attempt+1) * time.Second)
}
if err != nil {
    return nil, fmt.Errorf("failed to load API templates: %v", err)
}
Defensive patterns

Strategy: retry

Validate before calling

if len(apiIds) == 0 {
    return nil, errors.New("no API template ids to preload")
}
if err := g.DB().Ping(ctx); err != nil {
    return nil, fmt.Errorf("db unavailable: %w", err)
}

Try / catch

data, err := preloadData(ctx, apiIds)
if err != nil {
    var dbErr goatdb.Error
    if errors.As(err, &dbErr) && isTransient(dbErr) {
        // requeue and retry later
    }
    return err
}

Prevention

When it happens

Trigger: ProcessApiMailQueue -> preloadData while the SELECT ... WHERE id IN (apiIds) on api_templates fails: DB down/restarting, connection pool exhausted, TLS/auth failure, table missing or column renamed by a migration, or read-only replica errors.

Common situations: Postgres container restarting during batch processing; app database user lacking SELECT on api_templates; schema drift after an upgrade; network partition between app and DB under load.

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


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