{"record":{"id":"0fbd973de2ee30de","repo":"vxcontrol/pentagi","slug":"failed-to-set-subtask-d-status-to-failed-w","errorCode":null,"errorMessage":"failed to set subtask %d status to failed: %w","messagePattern":"failed to set subtask (.+?) status to failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/subtask.go","lineNumber":346,"sourceCode":"\t\t_ = stw.SetStatus(ctx, database.SubtaskStatusWaiting)\n\t\treturn fmt.Errorf(\"failed to perform agent chain for subtask %d: %w\", subtaskID, err)\n\t}\n\n\tswitch performResult {\n\tcase providers.PerformResultWaiting:\n\t\tif err := stw.SetStatus(ctx, database.SubtaskStatusWaiting); err != nil {\n\t\t\tstw.handleInterrupting(err)\n\t\t\treturn err\n\t\t}\n\tcase providers.PerformResultDone:\n\t\tif err := stw.SetStatus(ctx, database.SubtaskStatusFinished); err != nil {\n\t\t\tstw.handleInterrupting(err)\n\t\t\treturn fmt.Errorf(\"failed to set subtask %d status to finished: %w\", subtaskID, err)\n\t\t}\n\tcase providers.PerformResultError:\n\t\tif err := stw.SetStatus(ctx, database.SubtaskStatusFailed); err != nil {\n\t\t\tstw.handleInterrupting(err)\n\t\t\treturn fmt.Errorf(\"failed to set subtask %d status to failed: %w\", subtaskID, err)\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown perform result: %d\", performResult)\n\t}\n\n\treturn nil\n}\n\n// handleInterrupting sets this subtask (and task/flow via SetStatus back-propagation)\n// to Waiting when err is context.Canceled or context.DeadlineExceeded. Use after the subtask\n// was advanced past Waiting (e.g. Running) but the run aborts before PerformAgentChain's\n// normal error handler, or when a late SetStatus fails with a context interruption.\nfunc (stw *subtaskWorker) handleInterrupting(err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\tif !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {\n\t\treturn","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtask.go#L328-L364","documentation":"Wraps the error from subtaskWorker.SetStatus when the worker tried to mark a subtask as 'failed' after the agent chain reported PerformResultError. The status write itself failed, so the subtask may remain Running instead of Failed, leaving the flow in an inconsistent state. handleInterrupting resets the subtask to Waiting only when the underlying error is a context interruption.","triggerScenarios":"subtaskWorker.Run() receives providers.PerformResultError from PerformAgentChain and SetStatus(ctx, SubtaskStatusFailed) fails — context canceled during the update, DB unreachable, or the subtask row was concurrently changed.","commonSituations":"Agent tool execution fails (e.g. Docker container error) at the same moment the user cancels the flow; DB connection pool exhaustion; the task was deleted concurrently; repeated failures during a Postgres failover.","solutions":["Inspect the wrapped root error for the DB failure and restore DB connectivity, then reload the flow — LoadSubtasks will pick the subtask up in its persisted state.","If the cause is context cancellation, the worker already reset the subtask to Waiting; re-run the flow or the subtask.","Check for concurrent status writers (flow cancellation + worker SetStatus racing) and ensure only the worker mutates subtask status.","If the subtask is stuck in 'running' in the DB, use the interrupt/reset path or manually reset it to created/waiting before retrying."],"exampleFix":"// before: ignoring the error, leaving subtask stuck running\n_ = stw.Run(ctx)\n// after: handle and verify state before retrying\nif err := stw.Run(ctx); err != nil {\n    logrus.WithError(err).Error(\"subtask run failed\")\n    // reload from DB to see actual persisted status before retrying\n    stc.LoadSubtasks(ctx, taskID, updater)\n}","handlingStrategy":"try-catch","validationCode":"if stw.IsCompleted() { return nil }\nif err := db.PingContext(ctx); err != nil { return err }","typeGuard":"func isInterrupting(err error) bool {\n    return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"if err := stw.Run(ctx); err != nil {\n    logrus.WithError(err).WithField(\"subtask_id\", id).Error(\"subtask run failed\")\n    // reload persisted state to learn the true status before any retry\n    _ = stc.LoadSubtasks(ctx, taskID, updater)\n}","preventionTips":["Always reload subtask status from the DB after a failed run before retrying.","Keep a single writer for subtask status (the worker) to avoid racing updates.","Alert on DB errors during status writes — a stuck 'running' subtask blocks the whole flow.","Provide an operator path to reset stuck subtasks to waiting."],"tags":["database","subtask","state-transition","failure-handling"],"backgroundTag":"subtask-status-update-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}