vxcontrol/pentagi · error

failed to set subtask %d status: %w

Error message

failed to set subtask %d status: %w

What it means

SetStatus persists a new status for the subtask via the database before applying in-memory worker state; this error wraps a failure of that status-update call. Callers are Run, handleInterrupting, and Finish — i.e. every lifecycle transition of the subtask goes through here, so a DB problem blocks the whole subtask lifecycle.

Source

Thrown at backend/pkg/controller/subtask.go:209

	})
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			// Replacement can delete the row before this stale worker finishes.
			// Treat it as complete to keep task shutdown idempotent.
			logrus.WithContext(ctx).WithFields(logrus.Fields{
				"subtask_id":       stw.subtaskCtx.SubtaskID,
				"requested_status": status,
			}).Warn("subtask no longer exists in the database, treating as already finished")

			stw.mx.Lock()
			stw.completed = true
			stw.waiting = false
			stw.mx.Unlock()

			return nil
		}

		return fmt.Errorf("failed to set subtask %d status: %w", stw.subtaskCtx.SubtaskID, err)
	}

	stw.mx.Lock()
	defer stw.mx.Unlock()

	switch status {
	case database.SubtaskStatusRunning:
		stw.completed = false
		stw.waiting = false
		err = stw.updater.SetStatus(ctx, database.TaskStatusRunning)
	case database.SubtaskStatusWaiting:
		stw.completed = false
		stw.waiting = true
		err = stw.updater.SetStatus(ctx, database.TaskStatusWaiting)
	case database.SubtaskStatusFinished, database.SubtaskStatusFailed:
		stw.completed = true
		stw.waiting = false
		// statuses Finished and Failed will be produced by stack from Run function call

View on GitHub (pinned to ea665308ba)

Solutions

  1. Inspect the wrapped DB error; if transient (connection/lock timeout), retry the status transition.
  2. Ensure only one worker instance controls a given subtask (see error 141's flow-worker map) to avoid lock contention.
  3. Verify DB health, pool sizing, and lock_timeout settings for long transactions.
  4. Check that the subtask row still exists; if the parent flow was removed, abort the worker cleanly.

Example fix

// before
if err := stw.SetStatus(ctx, database.SubtaskStatusFinished); err != nil {
    return err
}
// after
if err := stw.SetStatus(ctx, database.SubtaskStatusFinished); err != nil {
    if isTransientDBError(err) {
        return retry(ctx, 3, backoff, func() error {
            return stw.SetStatus(ctx, database.SubtaskStatusFinished)
        })
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check DB reachability before lifecycle transitions
if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("db unavailable, deferring status change: %w", err)
}

Type guard

func IsStatusUpdateFailure(err error) bool {
    return strings.Contains(err.Error(), "failed to set subtask")
}

Try / catch

if err := stw.SetStatus(ctx, database.SubtaskStatusFinished); err != nil {
    if isTransientDBError(err) {
        return retry(ctx, 3, backoff, func() error {
            return stw.SetStatus(ctx, database.SubtaskStatusFinished)
        })
    }
    log.Error("status update failed permanently", "subtask", stw.SubtaskID, "err", err)
    return err
}

Prevention

When it happens

Trigger: Any lifecycle transition (run, interrupt, finish) when the underlying UPDATE fails: DB down, row locked by a concurrent writer, transaction timeout, or the subtask row was deleted mid-run.

Common situations: Postgres failover or connection-pool exhaustion during a long-running subtask; two workers controlling the same subtask causing lock waits; migrations/schema drift; the flow was deleted while its subtask was still running.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/6d843a404d1f67a4. Report an issue: GitHub.