vxcontrol/pentagi · error

failed to set task status in back propagation: %w

Error message

failed to set task status in back propagation: %w

What it means

SetStatus first persists the subtask status, then propagates a matching task-level status via updater.SetStatus. When the subtask row updated fine but the task-status back-propagation call fails, the underlying error is wrapped as 'failed to set task status in back propagation'. This means the task-level state machine is now out of sync with the subtask's persisted status.

Source

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

	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
	default:
		// status Created is not possible to set by this call
		return fmt.Errorf("unsupported subtask status: %s", status)
	}
	if err != nil {
		return fmt.Errorf("failed to set task status in back propagation: %w", err)
	}

	return nil
}

func (stw *subtaskWorker) GetResult(ctx context.Context) (string, error) {
	subtask, err := stw.subtaskCtx.DB.GetSubtask(ctx, stw.subtaskCtx.SubtaskID)
	if err != nil {
		return "", err
	}

	return subtask.Result, nil
}

func (stw *subtaskWorker) SetResult(ctx context.Context, result string) error {
	_, err := stw.subtaskCtx.DB.UpdateSubtaskResult(ctx, database.UpdateSubtaskResultParams{
		Result: result,
		ID:     stw.subtaskCtx.SubtaskID,

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check the wrapped error with errors.Is for context.Canceled/context.DeadlineExceeded; if so retry SetStatus with a fresh context.Background()+timeout (the codebase does this in handleInterrupting).
  2. Verify DB connectivity and run the pending goose migrations; UpdateTaskStatus failing with undefined-table/column means migrations did not run.
  3. Confirm the parent task row still exists; if the flow/task was deleted while the subtask worker ran, treat the worker as stale and stop it instead of retrying.
  4. Retry SetStatus once with backoff — transient pq/pgx errors are safe to retry since the update is idempotent by ID.

Example fix

// before
if err := stw.SetStatus(ctx, database.SubtaskStatusRunning); err != nil {
	return err
}
// after
if err := stw.SetStatus(ctx, database.SubtaskStatusRunning); err != nil {
	if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
		resetCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		return stw.SetStatus(resetCtx, database.SubtaskStatusRunning)
	}
	return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check DB reachability and parent task existence before mutating status
var exists bool
err := db.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM tasks WHERE id = $1)`, taskID).Scan(&exists)
if err != nil || !exists {
	// task row missing or DB unreachable — do not attempt SetStatus
}

Type guard

func isContextErr(err error) bool {
	return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)
}

Try / catch

if err := worker.SetStatus(ctx, status); err != nil {
	if isContextErr(err) {
		resetCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		err = worker.SetStatus(resetCtx, status)
	}
	if err != nil {
		logrus.WithError(err).Error("task status back-propagation failed")
	}
}

Prevention

When it happens

Trigger: Calling SetStatus(ctx, SubtaskStatusRunning) or SetStatus(ctx, SubtaskStatusWaiting) (via Run, handleInterrupting, or Finish) when stw.updater.SetStatus returns an error — typically a DB failure on UpdateTaskStatus (connection loss, transaction abort, ctx cancellation mid-query) or the task row no longer existing.

Common situations: PostgreSQL restart or connection pool exhaustion during a long-running flow; a cancelled/expired ctx passed into SetStatus so the task UPDATE aborts; concurrent replacement of the task deleting the task row while a stale subtask worker finishes.

Related errors


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