vxcontrol/pentagi · error

failed to delete subtasks for task %d: %w

Error message

failed to delete subtasks for task %d: %w

What it means

RefineSubtasks deletes the obsolete subtask IDs in the database before inserting the refined plan. This error wraps a failure of DeleteSubtasks. Because delete and insert are not yet transactional (see the TODO), a failure here leaves the task in the pre-refinement state, which is recoverable by re-running.

Source

Thrown at backend/pkg/controller/subtasks.go:121

	plan, err := stc.taskCtx.Provider.RefineSubtasks(ctx, stc.taskCtx.TaskID)
	if err != nil {
		return fmt.Errorf("failed to refine subtasks for task %d: %w", stc.taskCtx.TaskID, err)
	}

	if len(plan) == 0 {
		return nil // no subtasks refined
	}

	subtaskIDs := make([]int64, 0, len(subtasks))
	for _, subtask := range subtasks {
		if subtask.Status == database.SubtaskStatusCreated {
			subtaskIDs = append(subtaskIDs, subtask.ID)
		}
	}

	err = stc.taskCtx.DB.DeleteSubtasks(ctx, subtaskIDs)
	if err != nil {
		return fmt.Errorf("failed to delete subtasks for task %d: %w", stc.taskCtx.TaskID, err)
	}
	stc.InvalidateSubtasks(subtaskIDs)

	// TODO: change it to insert subtasks in transaction and union it with delete ones
	for _, info := range plan {
		_, err := stc.taskCtx.DB.CreateSubtask(ctx, database.CreateSubtaskParams{
			Status:      database.SubtaskStatusCreated,
			TaskID:      stc.taskCtx.TaskID,
			Title:       info.Title,
			Description: info.Description,
		})
		if err != nil {
			return fmt.Errorf("failed to create subtask for task %d: %w", stc.taskCtx.TaskID, err)
		}
	}

	return nil
}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check the wrapped DB error in logs for constraint/connection details
  2. Retry RefineSubtasks — deletes happen before inserts so no partial plan exists
  3. Verify FK constraints on subtasks table (dependent rows must be deleted first)
  4. Run delete+insert inside a single transaction to make refinement atomic

Example fix

// before
err = stc.taskCtx.DB.DeleteSubtasks(ctx, subtaskIDs)
if err != nil {
    return fmt.Errorf("failed to delete subtasks for task %d: %w", stc.taskCtx.TaskID, err)
}
// after
if len(subtaskIDs) > 0 {
    err = stc.taskCtx.DB.DeleteSubtasks(ctx, subtaskIDs)
    if err != nil {
        return fmt.Errorf("failed to delete subtasks for task %d: %w", stc.taskCtx.TaskID, err)
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if len(subtaskIDs) == 0 { /* skip delete */ }

Try / catch

if err := stc.taskCtx.DB.DeleteSubtasks(ctx, subtaskIDs); err != nil {
    var pqErr *pq.Error
    if errors.As(err, &pqErr) { log.Error("delete failed", "constraint", pqErr.Constraint) }
    return fmt.Errorf("failed to delete subtasks for task %d: %w", taskID, err)
}

Prevention

When it happens

Trigger: DeleteSubtasks fails due to DB connection loss, context cancellation, FK constraint blocking a delete, or an empty/large ID list hitting query limits.

Common situations: Postgres connection pool exhausted during concurrent flows; long-running refine transaction timing out; DB read replica used for writes by misconfiguration.

Related errors


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