{"record":{"id":"4820cc0b053b5aa3","repo":"vxcontrol/pentagi","slug":"no-subtasks-found-for-task-d-w","errorCode":null,"errorMessage":"no subtasks found for task %d: %w","messagePattern":"no subtasks found for task (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"backend/pkg/controller/subtasks.go","lineNumber":52,"sourceCode":"func NewSubtaskController(taskCtx *TaskContext) SubtaskController {\n\treturn &subtaskController{\n\t\tmx:       &sync.Mutex{},\n\t\ttaskCtx:  taskCtx,\n\t\tsubtasks: make(map[int64]SubtaskWorker),\n\t}\n}\n\nfunc (stc *subtaskController) LoadSubtasks(ctx context.Context, taskID int64, updater TaskUpdater) error {\n\tstc.mx.Lock()\n\tdefer stc.mx.Unlock()\n\n\tsubtasks, err := stc.taskCtx.DB.GetTaskSubtasks(ctx, taskID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get subtasks for task %d: %w\", taskID, err)\n\t}\n\n\tif len(subtasks) == 0 {\n\t\treturn fmt.Errorf(\"no subtasks found for task %d: %w\", taskID, ErrNothingToLoad)\n\t}\n\n\tfor _, subtask := range subtasks {\n\t\tst, err := LoadSubtaskWorker(ctx, subtask, stc.taskCtx, updater)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, ErrNothingToLoad) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\treturn fmt.Errorf(\"failed to create subtask worker: %w\", err)\n\t\t}\n\n\t\tstc.subtasks[subtask.ID] = st\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtasks.go#L34-L70","documentation":"Returned when GetTaskSubtasks succeeds but returns zero rows for the task, wrapped together with the sentinel ErrNothingToLoad. It signals there are no subtasks to restore into workers — the task has no plan generated yet (or its subtasks were never created).","triggerScenarios":"subtaskController.LoadSubtasks is called for a taskID whose subtasks table rows are empty — e.g. resuming a flow before GenerateSubtasks ever ran, or after all subtasks were deleted.","commonSituations":"Server restart while a task was still in 'created' state with no generated plan; calling the resume/load API on a freshly created flow; a prior GenerateSubtasks failure left the task with no subtasks.","solutions":["Check errors.Is(err, ErrNothingToLoad) at the call site and, if so, call GenerateSubtasks instead of treating it as a hard failure.","Verify the task actually has subtasks in the DB (SELECT * FROM subtasks WHERE task_id = ...) before resuming.","If subtasks were deleted unintentionally, re-generate them via the flow's generate-plan path."],"exampleFix":"// before\nif err := stc.LoadSubtasks(ctx, taskID, updater); err != nil {\n    return err\n}\n// after\nif err := stc.LoadSubtasks(ctx, taskID, updater); err != nil {\n    if errors.Is(err, ErrNothingToLoad) {\n        return stc.GenerateSubtasks(ctx)\n    }\n    return err\n}","handlingStrategy":"fallback","validationCode":"n, err := db.GetTaskSubtaskCount(ctx, taskID)\nif err == nil && n == 0 {\n    // nothing to load — generate instead\n}","typeGuard":"func isNothingToLoad(err error) bool {\n    return errors.Is(err, controller.ErrNothingToLoad)\n}","tryCatchPattern":"if err := stc.LoadSubtasks(ctx, taskID, updater); err != nil {\n    if isNothingToLoad(err) {\n        return stc.GenerateSubtasks(ctx) // fallback: create a plan\n    }\n    return err\n}","preventionTips":["Check for ErrNothingToLoad explicitly instead of string-matching error text.","Only resume flows that have progressed past plan generation.","Call GenerateSubtasks as the fallback when a task has no subtasks yet.","Never hard-fail a resume on an empty subtask list — it's a normal state."],"tags":["subtask","empty-result","sentinel-error","flow-resume"],"backgroundTag":"no-rows-found","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}