vxcontrol/pentagi · error

no subtasks generated for task %d

Error message

no subtasks generated for task %d

What it means

Returned when Provider.GenerateSubtasks succeeds but returns an empty plan — the LLM produced no usable subtasks. The controller refuses to continue with zero subtasks since the task would have nothing to execute. Note the error carries no wrapped cause; it is a semantic validation of the provider output.

Source

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

			}

			return fmt.Errorf("failed to create subtask worker: %w", err)
		}

		stc.subtasks[subtask.ID] = st
	}

	return nil
}

func (stc *subtaskController) GenerateSubtasks(ctx context.Context) error {
	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
}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Retry generation — LLMs can return empty output nondeterministically; a re-run often yields a valid plan.
  2. Improve the task title/description to be more specific so the model can decompose it.
  3. Switch to a stronger model/provider for plan generation in Settings.
  4. Review the provider's GenerateSubtasks parsing logic — a response-format mismatch may silently drop all entries.
  5. Adjust the plan-generation prompt template in the prompt settings.

Example fix

// before: single-shot generation
if err := stc.GenerateSubtasks(ctx); err != nil { return err }
// after: validate inputs and retry once on empty plan
if task.Description == "" { return fmt.Errorf("task %d has no description", taskID) }
if err := stc.GenerateSubtasks(ctx); err != nil {
    if strings.Contains(err.Error(), "no subtasks generated") {
        return stc.GenerateSubtasks(ctx)
    }
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// ensure the task has enough context for the model to decompose
if len(strings.TrimSpace(task.Description)) < 20 {
    return fmt.Errorf("task description too short for subtask generation")
}

Type guard

func hasUsablePlan(plan []providers.SubtaskInfo) bool {
    return len(plan) > 0
}

Try / catch

err := stc.GenerateSubtasks(ctx)
if err != nil && strings.Contains(err.Error(), "no subtasks generated") {
    // empty LLM output: retry once, then surface to the user
    if retryErr := stc.GenerateSubtasks(ctx); retryErr != nil {
        return retryErr
    }
}

Prevention

When it happens

Trigger: GenerateSubtasks receives len(plan) == 0 after a successful provider call — the model returned an empty list, or the provider's parsing produced no entries from the response.

Common situations: Model too weak/small to follow the plan-generation prompt; temperature/prompt configuration producing empty structured output; custom provider returning an empty array on parse quirks; task description too vague for the model to decompose.

Related errors


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