vxcontrol/pentagi · error

failed to create subtask for task %d: %w

Error message

failed to create subtask for task %d: %w

What it means

Wraps the error from DB.CreateSubtask while persisting each subtask from a generated plan. The plan was produced successfully but writing a subtask row to PostgreSQL failed, aborting generation partway (creation is not transactional — see the TODO in the source). Some subtasks may already have been created, so the task can be left with a partial plan.

Source

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

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

	if len(plan) == 0 {
		return fmt.Errorf("no subtasks generated for task %d", stc.taskCtx.TaskID)
	}

	// TODO: change it to insert subtasks in transaction
	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
}

func (stc *subtaskController) RefineSubtasks(ctx context.Context) error {
	subtasks, err := stc.taskCtx.DB.GetTaskSubtasks(ctx, stc.taskCtx.TaskID)
	if err != nil {
		return fmt.Errorf("failed to get task %d subtasks: %w", stc.taskCtx.TaskID, err)
	}

	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 {

View on GitHub (pinned to ea665308ba)

Solutions

  1. Inspect the wrapped error; if transient (connection/context), retry GenerateSubtasks — but first check for a partial plan already in the DB to avoid duplicates.
  2. Check errors.Is(err, context.Canceled): the caller canceled during insertion; the partially created subtasks remain and can be loaded with LoadSubtasks.
  3. Verify the subtasks schema is up to date (goose migrations) so CreateSubtaskParams matches the table.
  4. If duplicates from partial inserts are the issue, delete the task's subtasks before regenerating.
  5. Contribute/apply the transactional insert (the TODO) so plan creation is all-or-nothing.

Example fix

// before: partial inserts possible
for _, info := range plan {
    _, err := stc.taskCtx.DB.CreateSubtask(ctx, params)
    if err != nil {
        return fmt.Errorf("failed to create subtask for task %d: %w", taskID, err)
    }
}
// after: check for existing partial plan before regenerating
existing, _ := stc.taskCtx.DB.GetTaskSubtasks(ctx, taskID)
if len(existing) > 0 {
    return nil // plan partially created; resume instead of duplicating
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ctx.Err() != nil { return ctx.Err() }
if err := db.PingContext(ctx); err != nil { return err }
// avoid duplicating a partial plan
existing, _ := db.GetTaskSubtasks(ctx, taskID)
if len(existing) > 0 { return ErrPlanExists }

Type guard

func isTransientInsertError(err error) bool {
    var pgErr *pgconn.PgError
    return errors.As(err, &pgErr) &&
        (pgErr.Code == "08000" || pgErr.Code == "08006" || pgErr.Code == "40001")
}

Try / catch

if err := stc.GenerateSubtasks(ctx); err != nil {
    if strings.Contains(err.Error(), "failed to create subtask") {
        // partial plan may exist: inspect DB and clean up or resume
        logrus.WithError(err).WithField("task_id", taskID).Error("partial subtask insert")
        return stc.reconcilePartialPlan(ctx, taskID)
    }
    return err
}

Prevention

When it happens

Trigger: In GenerateSubtasks' loop, stc.taskCtx.DB.CreateSubtask(ctx, CreateSubtaskParams{...}) fails for one of the plan entries — DB connection loss, context cancellation mid-loop, unique constraint violation, or schema mismatch.

Common situations: DB restart or connection pool exhaustion mid-loop; the caller's context canceled halfway through inserting the plan; migration drift making CreateSubtaskParams incompatible with the table; long plans hitting a statement timeout.

Related errors


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