Billionmail/BillionMail · error

task %d not found

Error message

task %d not found

What it means

UpdateTaskThreads checks the task returned by GetTaskInfo and rejects the update with "task %d not found" when the record is nil or has Id == 0. This distinguishes a successful lookup with no matching row from a hard database failure.

Source

Thrown at core/internal/service/batch_mail/task_executor.go:1475

func (e *TaskExecutor) UpdateTaskThreads(taskId int, threads int) error {
	// parameter validation
	if threads <= 0 {
		return fmt.Errorf("threads must be greater than zero")
	}

	if threads > 100 {
		return fmt.Errorf("threads must be less than 100")
	}

	// get task info
	task, err := GetTaskInfo(context.Background(), taskId)
	if err != nil {
		return fmt.Errorf("get task info failed: %w", err)
	}

	if task == nil || task.Id == 0 {
		return fmt.Errorf("task %d not found", taskId)
	}

	// record current pool status
	var oldPoolSize int
	var runningWorkers int
	if e.pool != nil {
		oldPoolSize = e.pool.Cap()
		runningWorkers = e.pool.Running()
	}

	// new threads
	newThreads := threads
	// calculate new rate limit - 20 emails per thread per second
	targetSendPerThreadPerSecond := 20
	newRate := newThreads * targetSendPerThreadPerSecond * 60

	// create new rate controller
	e.rateController = NewSimpleRateController(newRate)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Verify the taskId exists (query the tasks table or refresh the task list).
  2. In the caller, validate taskId > 0 and handle 'not found' by reloading current tasks before updating.
  3. If the task was deleted, recreate it or drop the pending update rather than retrying.
  4. Check for soft-delete/tenant filters in GetTaskInfo that may hide the row legitimately.

Example fix

// before: no existence check client-side
await api.updateTaskThreads(deletedTaskId, 10)
// after
const task = tasks.value.find(t => t.id === taskId)
if (!task) { message.error('Task no longer exists'); return }
await api.updateTaskThreads(task.id, 10)
Defensive patterns

Strategy: validation

Validate before calling

// Go: confirm the task exists before updating
task, err := GetTaskInfo(context.Background(), taskId)
if err != nil { return err }
if task == nil || task.Id == 0 {
    return fmt.Errorf("task %d not found; refresh task list", taskId)
}

Type guard

func taskExists(taskId int, tasks []*model.Task) bool {
    return taskId > 0 && slices.ContainsFunc(tasks, func(t *model.Task) bool { return t.Id == taskId })
}

Try / catch

if err := executor.UpdateTaskThreads(taskId, threads); err != nil {
    if strings.Contains(err.Error(), fmt.Sprintf("task %d not found", taskId)) {
        // reload tasks in UI / remove stale entry; do not retry blindly
        return ErrTaskGone
    }
    return err
}

Prevention

When it happens

Trigger: Calling UpdateTaskThreads with a taskId that does not exist in the tasks table, references a deleted task, or where GetTaskInfo returns an empty struct for an id-only filter miss.

Common situations: Stale UI list showing a task deleted by another admin; off-by-one or wrong-type ids (string id parsed to 0) from client requests; tasks removed by a cleanup job while an update was in flight.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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