{"record":{"id":"e53b18d2fa7accac","repo":"vxcontrol/pentagi","slug":"failed-to-get-task-planned-subtasks-w","errorCode":null,"errorMessage":"failed to get task planned subtasks: %w","messagePattern":"failed to get task planned subtasks: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/subtasks.go","lineNumber":147,"sourceCode":"\t\t\tTaskID:      stc.taskCtx.TaskID,\n\t\t\tTitle:       info.Title,\n\t\t\tDescription: info.Description,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create subtask for task %d: %w\", stc.taskCtx.TaskID, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (stc *subtaskController) PopSubtask(ctx context.Context, updater TaskUpdater) (SubtaskWorker, error) {\n\tstc.mx.Lock()\n\tdefer stc.mx.Unlock()\n\n\tsubtasks, err := stc.taskCtx.DB.GetTaskPlannedSubtasks(ctx, stc.taskCtx.TaskID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get task planned subtasks: %w\", err)\n\t}\n\n\tif len(subtasks) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tstdb := subtasks[0]\n\tif st, ok := stc.subtasks[stdb.ID]; ok {\n\t\treturn st, nil\n\t}\n\n\tst, err := NewSubtaskWorker(ctx, stc.taskCtx, stdb.ID, stdb.Title, stdb.Description, updater)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create subtask worker: %w\", err)\n\t}\n\n\tstc.subtasks[stdb.ID] = st\n","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtasks.go#L129-L165","documentation":"PopSubtask fetches the next planned subtask for a task via GetTaskPlannedSubtasks under the controller mutex. This error wraps a read failure of that query. It is returned to the worker loop, so a persistent DB failure prevents any subtask from being dispatched.","triggerScenarios":"Calling PopSubtask when the DB is unreachable, the query context is cancelled by flow shutdown, or the planned-subtasks query fails to scan rows.","commonSituations":"Postgres restart while workers are polling for work; flow context cancelled during graceful shutdown so ctx.Err() surfaces as the wrapped error; pool exhaustion with many concurrent flows.","solutions":["Check the wrapped cause — connection errors warrant DB restart/reconnect handling","Distinguish context.Canceled from real DB errors and treat cancellation as benign","Ensure connection pool size matches number of concurrent flow workers","Retry PopSubtask; it holds a mutex so it is safe to call again"],"exampleFix":"// before\nsubtasks, err := stc.taskCtx.DB.GetTaskPlannedSubtasks(ctx, stc.taskCtx.TaskID)\nif err != nil {\n    return nil, fmt.Errorf(\"failed to get task planned subtasks: %w\", err)\n}\n// after\nsubtasks, err := stc.taskCtx.DB.GetTaskPlannedSubtasks(ctx, stc.taskCtx.TaskID)\nif err != nil {\n    if errors.Is(err, context.Canceled) {\n        return nil, err\n    }\n    return nil, fmt.Errorf(\"failed to get task planned subtasks: %w\", err)\n}","handlingStrategy":"retry","validationCode":"select { case <-ctx.Done(): return ctx.Err(); default: }","typeGuard":null,"tryCatchPattern":"subtasks, err := stc.taskCtx.DB.GetTaskPlannedSubtasks(ctx, taskID)\nif err != nil {\n    if errors.Is(err, context.Canceled) { return nil, err }\n    return nil, fmt.Errorf(\"failed to get task planned subtasks: %w\", err)\n}","preventionTips":["Size the DB pool for the number of concurrent workers","Handle graceful shutdown by cancelling worker loops before DB teardown","Add retry with backoff around PopSubtask in worker loops"],"tags":["database","worker","subtask-dispatch"],"backgroundTag":"database-query-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}