vxcontrol/pentagi · error

failed to set flow %d status: %w

Error message

failed to set flow %d status: %w

What it means

flowWorker.SetStatus persists a new flow status via UpdateFlowStatus and then republishes the flow. If the UPDATE fails (DB error or cancelled context), the error is wrapped as 'failed to set flow %d status'. Callers (Finish, worker loop, processInput) rely on this to transition lifecycle state, so a failure here can leave a flow stuck in its previous status.

Source

Thrown at backend/pkg/controller/flow.go:547

func (fw *flowWorker) GetStatus(ctx context.Context) (database.FlowStatus, error) {
	flow, err := fw.flowCtx.DB.GetUserFlow(ctx, database.GetUserFlowParams{
		UserID: fw.flowCtx.UserID,
		ID:     fw.flowCtx.FlowID,
	})
	if err != nil {
		return database.FlowStatusFailed, err
	}

	return flow.Status, nil
}

func (fw *flowWorker) SetStatus(ctx context.Context, status database.FlowStatus) error {
	flow, err := fw.flowCtx.DB.UpdateFlowStatus(ctx, database.UpdateFlowStatusParams{
		Status: status,
		ID:     fw.flowCtx.FlowID,
	})
	if err != nil {
		return fmt.Errorf("failed to set flow %d status: %w", fw.flowCtx.FlowID, err)
	}

	containers, err := fw.flowCtx.DB.GetFlowContainers(ctx, fw.flowCtx.FlowID)
	if err != nil {
		return fmt.Errorf("failed to get flow %d containers: %w", fw.flowCtx.FlowID, err)
	}

	fw.flowCtx.Publisher.FlowUpdated(ctx, flow, containers)

	return nil
}

// InvalidateTaskSubtasks drops stale workers after direct DB deletion,
// preventing delayed ErrNoRows failures.
func (fw *flowWorker) InvalidateTaskSubtasks(ctx context.Context, taskID int64, subtaskIDs []int64) {
	task, err := fw.tc.GetTask(ctx, taskID)
	if err != nil {
		return

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check the wrapped cause in logs: sql.ErrNoRows (flow deleted) vs driver/conn errors vs context.Canceled.
  2. Retry the status update with context.WithoutCancel(ctx) during shutdown paths so the final status is persisted.
  3. Confirm the flow still exists (not soft-deleted) if ErrNoRows.
  4. Verify Postgres connectivity and pool limits if failures cluster under load.
  5. Re-run migrations if the flows table/schema is missing columns.

Example fix

// before
err := fw.SetStatus(ctx, database.FlowStatusFinished) // ctx cancelled at shutdown, status lost
// after
err := fw.SetStatus(context.WithoutCancel(ctx), database.FlowStatusFinished)
if err != nil {
    log.WithError(err).Error("failed to persist final flow status")
}
Defensive patterns

Strategy: retry

Validate before calling

if _, err := db.GetFlow(ctx, flowID); err != nil {
    return fmt.Errorf("flow %d gone, cannot set status: %w", flowID, err)
}

Try / catch

err := fw.SetStatus(ctx, database.FlowStatusFinished)
if err != nil {
    if errors.Is(err, context.Canceled) {
        err = fw.SetStatus(context.WithoutCancel(ctx), database.FlowStatusFinished)
    }
    if err != nil { log.WithError(err).Error("flow status not persisted") }
}

Prevention

When it happens

Trigger: UpdateFlowStatus returns an error: Postgres unreachable, context cancelled (e.g. worker shutting down or client-driven ctx), row lock contention, or the flow row was deleted concurrently.

Common situations: Graceful shutdown cancelling ctx mid-update; DB failover/connection pool exhaustion during heavy runs; flow soft-deleted by the user in the UI while its worker tries to set status; migration missing.

Related errors


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