{"record":{"id":"edbbd49eb054db0e","repo":"vxcontrol/pentagi","slug":"failed-to-set-task-status-in-back-propagation-w","errorCode":null,"errorMessage":"failed to set task status in back propagation: %w","messagePattern":"failed to set task status in back propagation: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/subtask.go","lineNumber":233,"sourceCode":"\tswitch status {\n\tcase database.SubtaskStatusRunning:\n\t\tstw.completed = false\n\t\tstw.waiting = false\n\t\terr = stw.updater.SetStatus(ctx, database.TaskStatusRunning)\n\tcase database.SubtaskStatusWaiting:\n\t\tstw.completed = false\n\t\tstw.waiting = true\n\t\terr = stw.updater.SetStatus(ctx, database.TaskStatusWaiting)\n\tcase database.SubtaskStatusFinished, database.SubtaskStatusFailed:\n\t\tstw.completed = true\n\t\tstw.waiting = false\n\t\t// statuses Finished and Failed will be produced by stack from Run function call\n\tdefault:\n\t\t// status Created is not possible to set by this call\n\t\treturn fmt.Errorf(\"unsupported subtask status: %s\", status)\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set task status in back propagation: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (stw *subtaskWorker) GetResult(ctx context.Context) (string, error) {\n\tsubtask, err := stw.subtaskCtx.DB.GetSubtask(ctx, stw.subtaskCtx.SubtaskID)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn subtask.Result, nil\n}\n\nfunc (stw *subtaskWorker) SetResult(ctx context.Context, result string) error {\n\t_, err := stw.subtaskCtx.DB.UpdateSubtaskResult(ctx, database.UpdateSubtaskResultParams{\n\t\tResult: result,\n\t\tID:     stw.subtaskCtx.SubtaskID,","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtask.go#L215-L251","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif err := stw.SetStatus(ctx, database.SubtaskStatusRunning); err != nil {\n\treturn err\n}\n// after\nif err := stw.SetStatus(ctx, database.SubtaskStatusRunning); err != nil {\n\tif errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n\t\tresetCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\t\treturn stw.SetStatus(resetCtx, database.SubtaskStatusRunning)\n\t}\n\treturn err\n}","handlingStrategy":"try-catch","validationCode":"// check DB reachability and parent task existence before mutating status\nvar exists bool\nerr := db.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM tasks WHERE id = $1)`, taskID).Scan(&exists)\nif err != nil || !exists {\n\t// task row missing or DB unreachable — do not attempt SetStatus\n}","typeGuard":"func isContextErr(err error) bool {\n\treturn errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"if err := worker.SetStatus(ctx, status); err != nil {\n\tif isContextErr(err) {\n\t\tresetCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\t\terr = worker.SetStatus(resetCtx, status)\n\t}\n\tif err != nil {\n\t\tlogrus.WithError(err).Error(\"task status back-propagation failed\")\n\t}\n}","preventionTips":["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."],"tags":["go","database","state-machine","back-propagation"],"backgroundTag":"task-status-sync-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}