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
- 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).
- Verify DB connectivity and run the pending goose migrations; UpdateTaskStatus failing with undefined-table/column means migrations did not run.
- 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.
- 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
- Always pass a live (non-cancelled) context to SetStatus; use context.WithTimeout for status writes.
- Monitor DB connection-pool saturation before long flow runs.
- Ensure goose migrations run at startup so UpdateTaskStatus never hits missing tables.
- When replacing/deleting flows, stop subtask workers first to avoid racing status writes.
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
- failed to set flow %d status: %w
- token not found in database
- failed to create flow in DB: %w
- failed to get user %d: %w
- flow %d has status %s: loading aborted: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/edbbd49eb054db0e.
Report an issue: GitHub.