vxcontrol/pentagi · error

failed to get subtasks for task %d: %w

Error message

failed to get subtasks for task %d: %w

What it means

Wraps the error from DB.GetTaskSubtasks when loading all persisted subtask workers for a task into the subtaskController. Failure means the in-memory flow workers could not be restored — usually a database connectivity/query error. The taskID is included to help locate the offending task.

Source

Thrown at backend/pkg/controller/subtasks.go:48

	taskCtx  *TaskContext
	subtasks map[int64]SubtaskWorker
}

func NewSubtaskController(taskCtx *TaskContext) SubtaskController {
	return &subtaskController{
		mx:       &sync.Mutex{},
		taskCtx:  taskCtx,
		subtasks: make(map[int64]SubtaskWorker),
	}
}

func (stc *subtaskController) LoadSubtasks(ctx context.Context, taskID int64, updater TaskUpdater) error {
	stc.mx.Lock()
	defer stc.mx.Unlock()

	subtasks, err := stc.taskCtx.DB.GetTaskSubtasks(ctx, taskID)
	if err != nil {
		return fmt.Errorf("failed to get subtasks for task %d: %w", taskID, err)
	}

	if len(subtasks) == 0 {
		return fmt.Errorf("no subtasks found for task %d: %w", taskID, ErrNothingToLoad)
	}

	for _, subtask := range subtasks {
		st, err := LoadSubtaskWorker(ctx, subtask, stc.taskCtx, updater)
		if err != nil {
			if errors.Is(err, ErrNothingToLoad) {
				continue
			}

			return fmt.Errorf("failed to create subtask worker: %w", err)
		}

		stc.subtasks[subtask.ID] = st
	}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Restore PostgreSQL connectivity and retry LoadSubtasks (flows are resumable — nothing is lost).
  2. Check the wrapped error for SQLSTATE/context errors; if context.Canceled, the caller canceled too early.
  3. Verify migrations ran (goose) — an outdated subtasks schema breaks GetTaskSubtasks.
  4. Confirm the taskID is valid and the task row still exists at load time.

Example fix

// before: retrying immediately in a tight loop
for {
    if err := stc.LoadSubtasks(ctx, taskID, updater); err == nil { break }
}
// after: backoff + root-cause log
if err := stc.LoadSubtasks(ctx, taskID, updater); err != nil {
    logrus.WithError(err).WithField("task_id", taskID).Error("load failed")
    time.Sleep(backoff)
    return
}
Defensive patterns

Strategy: retry

Validate before calling

if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("database unreachable, cannot load task %d", taskID)
}
// taskID must be positive
if taskID <= 0 { return ErrInvalidTaskID }

Type guard

func isDBError(err error) bool {
    var pgErr *pgconn.PgError
    return errors.As(err, &pgErr) || errors.Is(err, context.Canceled)
}

Try / catch

err := stc.LoadSubtasks(ctx, taskID, updater)
if err != nil {
    if isDBError(err) {
        // transient: backoff and retry
        time.Sleep(2 * time.Second)
        return stc.LoadSubtasks(ctx, taskID, updater)
    }
    return err
}

Prevention

When it happens

Trigger: subtaskController.LoadSubtasks(ctx, taskID, updater) calls stc.taskCtx.DB.GetTaskSubtasks(ctx, taskID) and the SQLC query returns an error — connection failure, canceled context, or a schema mismatch.

Common situations: Postgres down or restarting when a server restart tries to resume flows; pgvector/DB migration not applied so the subtasks query fails; taskID referencing a task deleted concurrently between existence check and load; network partition between backend and DB.

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 vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/5943ac3f4f2c2651. Report an issue: GitHub.