{"record":{"id":"0b2f443d221f8470","repo":"vxcontrol/pentagi","slug":"failed-to-get-flow-subtasks-w-0b2f44","errorCode":null,"errorMessage":"failed to get flow subtasks: %w","messagePattern":"failed to get flow subtasks: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/flow_manager.go","lineNumber":99,"sourceCode":"\t\treturn t.buildSubtasksList(ctx, action.TaskID.PtrInt64(), verbose)\n\tcase FlowStatusDetailRunning:\n\t\treturn t.buildRunningInfo(ctx, verbose)\n\tcase FlowStatusDetailPlanned:\n\t\treturn t.buildPlannedList(ctx, action.TaskID.PtrInt64(), verbose)\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unknown detail level %q; use one of: summary, tasks, subtasks, running, planned\", action.Detail)\n\t}\n}\n\nfunc (t *flowStatusTool) buildSummary(ctx context.Context, verbose bool) (string, error) {\n\ttasks, err := t.db.GetFlowTasks(ctx, t.flowID)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to get flow tasks: %w\", err)\n\t}\n\n\tsubtasks, err := t.db.GetFlowSubtasks(ctx, t.flowID)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to get flow subtasks: %w\", err)\n\t}\n\n\ttaskCounts := map[string]int{\"created\": 0, \"running\": 0, \"waiting\": 0, \"finished\": 0, \"failed\": 0}\n\tvar activeTask *database.Task\n\tfor i, task := range tasks {\n\t\ttaskCounts[string(task.Status)]++\n\t\tif task.Status == database.TaskStatusRunning || task.Status == database.TaskStatusWaiting {\n\t\t\tactiveTask = &tasks[i]\n\t\t}\n\t}\n\n\tstCounts := map[string]int{\"created\": 0, \"running\": 0, \"waiting\": 0, \"finished\": 0, \"failed\": 0}\n\tvar activeST *database.Subtask\n\tfor i, st := range subtasks {\n\t\tstCounts[string(st.Status)]++\n\t\tif st.Status == database.SubtaskStatusRunning || st.Status == database.SubtaskStatusWaiting {\n\t\t\tactiveST = &subtasks[i]\n\t\t}","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/flow_manager.go#L81-L117","documentation":"This error is thrown by the get_flow_status tool's buildSummary (backend/pkg/tools/flow_manager.go:99) when the database query GetFlowSubtasks fails while assembling the flow summary. It wraps the underlying DB error with %w so the root cause (connection failure, timeout, SQL error) is preserved. It means the tool could not read the subtask list for the tool's flowID.","triggerScenarios":"Calling the get_flow_status tool with detail='summary' (Handle -> buildSummary) while t.db.GetFlowSubtasks(ctx, t.flowID) returns an error — e.g. PostgreSQL unreachable, statement timeout, context cancellation, or a schema mismatch after a failed migration.","commonSituations":"Database container down or restarting during heavy agent runs; connection-pool exhaustion under many concurrent flows; pgvector extension or migration version mismatch; context deadline exceeded because the caller's LLM request timeout is shorter than the DB latency.","solutions":["Check PostgreSQL connectivity and container health (docker compose ps, pg_isready) and restart the database if it is down.","Inspect the wrapped cause with errors.Unwrap / logs — fix the specific DB error (timeout, pool limits, bad credentials in .env).","Verify goose migrations ran to completion (backend/migrations/sql) so the subtasks table/schema matches the SQLC queries.","Increase DB pool size / statement timeout, or retry the tool call once the database is reachable."],"exampleFix":"// before\nsubtasks, err := t.db.GetFlowSubtasks(ctx, t.flowID)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"failed to get flow subtasks: %w\", err)\n}\n// after: retry transient DB failures once before failing the tool call\nsubtasks, err := t.db.GetFlowSubtasks(ctx, t.flowID)\nif err != nil && isTransientDBError(err) {\n\ttime.Sleep(500 * time.Millisecond)\n\tsubtasks, err = t.db.GetFlowSubtasks(ctx, t.flowID)\n}\nif err != nil {\n\treturn \"\", fmt.Errorf(\"failed to get flow subtasks: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// before relying on the tool, confirm DB reachability from the app host\nrows, err := db.QueryContext(ctx, \"SELECT 1 FROM subtasks LIMIT 1\")\nif err != nil {\n\treturn fmt.Errorf(\"database not ready for get_flow_status: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"// Go: classify the wrapped cause before reacting\nresult, err := tool.Handle(ctx, \"get_flow_status\", args)\nif err != nil {\n\tif errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {\n\t\treturn retryWithBackoff(ctx, err)\n\t}\n\tvar pgErr *pgconn.PgError\n\tif errors.As(err, &pgErr) && pgErr.Code == \"53300\" { // too_many_connections\n\t\treturn backoffAndRetry(ctx)\n\t}\n\treturn fmt.Errorf(\"get_flow_status failed: %w\", err)\n}","preventionTips":["Monitor PostgreSQL health (pg_isready probe, container restart policy) before agent runs.","Size the connection pool for concurrent flow goroutines and set sane statement timeouts.","Keep goose migrations applied on every deploy so schema matches SQLC queries.","Alert on wrapped DB errors from tool handlers rather than retrying blindly."],"tags":["database","postgresql","tool","flow-status"],"backgroundTag":"database-query-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}