Billionmail/BillionMail · error
failed to update task process status: %w
Error message
failed to update task process status: %w
What it means
UpdateTaskProcessStatus sets task_process on an email_tasks row during task processing. Any UPDATE error is wrapped as "failed to update task process status: %w". ProcessTask relies on this to track progress, so failures here leave task state stale even if sending continued.
Source
Thrown at core/internal/service/batch_mail/batch_mail.go:571
Update()
if err != nil {
return fmt.Errorf("failed to update task pause status: %w", err)
}
g.Log().Info(ctx, "Updated task %d pause status: isPaused=%v, task_process=%d", taskId, isPaused, processValue)
return nil
}
// UpdateTaskProcessStatus update task process status
func UpdateTaskProcessStatus(ctx context.Context, taskId int, status int) error {
_, err := g.DB().Model("email_tasks").
Where("id", taskId).
Data(g.Map{"task_process": status}).
Update()
if err != nil {
return fmt.Errorf("failed to update task process status: %w", err)
}
return nil
}
// GetTaskSendingStats get task sending stats (success count and failed count)
func GetTaskSendingStats(ctx context.Context, taskID int) (int, int, error) {
if taskID <= 0 {
return 0, 0, nil
}
// query success count
successQuery := g.DB().Model("mailstat_send_mails sm")
successQuery = successQuery.LeftJoin("mailstat_message_ids mid", "sm.postfix_message_id=mid.postfix_message_id")
successQuery = successQuery.LeftJoin("recipient_info ri", "mid.message_id=ri.message_id")
successQuery = successQuery.Where("ri.task_id", taskID)
successQuery = successQuery.Where("sm.status", "sent")
successQuery = successQuery.Where("sm.dsn LIKE '2.%'")View on GitHub (pinned to fc36c76c05)
Solutions
- Check the wrapped underlying error and DB connectivity
- Verify UPDATE permissions on email_tasks and primary-instance routing
- Increase statement/idle timeouts for long-running send workers
- Retry the status update (it is idempotent for a given status value)
Example fix
// before: single attempt, fire and forget failure path
_, err := g.DB().Model("email_tasks").Where("id", taskId).Data(g.Map{"task_process": status}).Update()
if err != nil {
return fmt.Errorf("failed to update task process status: %w", err)
}
// after: retry idempotent status update
var err error
for i := 0; i < 3; i++ {
if _, err = g.DB().Model("email_tasks").Where("id", taskId).Data(g.Map{"task_process": status}).Update(); err == nil {
return nil
}
time.Sleep(time.Duration(i+1) * 500 * time.Millisecond)
}
return fmt.Errorf("failed to update task process status: %w", err) Defensive patterns
Strategy: retry
Validate before calling
if taskId <= 0 {
return errors.New("invalid taskId")
}
if status < 0 {
return errors.New("invalid process status")
} Try / catch
if err := UpdateTaskProcessStatus(ctx, taskId, status); err != nil {
if isTransientDBError(err) {
// retry once or twice; the update is idempotent
}
log.Printf("task %d status update failed: %v", taskId, err)
return err
} Prevention
- Treat status updates as idempotent and retry transient failures
- Keep DB sessions alive for long send workers (heartbeat/pool pings)
- Check for table locks from analytics queries on email_tasks
- Alert on repeated status-update failures during campaigns
When it happens
Trigger: ProcessTask updating the process status while the DB UPDATE fails: connection drop mid-send, statement timeout, UPDATE permission denied, or trigger/constraint rejection on email_tasks.
Common situations: Long campaigns where the DB connection goes stale; failover during a large batch send; DB user restricted to read-only; table locks from analytics queries scanning email_tasks.
Related errors
- failed to update task pause status: %w
- failed to get all domains: %w
- fail to check domain: %w
- failed to load API templates: %v
- failed to load email templates: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/48c1f238fc96a933.
Report an issue: GitHub.