{"record":{"id":"45a466ca58e6a746","repo":"vxcontrol/pentagi","slug":"failed-to-get-subtask-result-w","errorCode":null,"errorMessage":"failed to get subtask result: %w","messagePattern":"failed to get subtask result: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/flow_manager.go","lineNumber":248,"sourceCode":"\t\tfmt.Fprintf(sb, \"Subtasks for task %d:\\n\\n\", *taskID)\n\t} else {\n\t\tfmt.Fprintf(sb, \"All subtasks for flow %d:\\n\\n\", t.flowID)\n\t}\n\tfor _, st := range subtasks {\n\t\tfmt.Fprintf(sb, \"Subtask ID: %d | Task ID: %d | Status: %s | Title: %s\\n\",\n\t\t\tst.ID, st.TaskID, st.Status, st.Title)\n\t\tif verbose {\n\t\t\tif st.Description != \"\" {\n\t\t\t\tdescription, err := t.getDescriptionText(ctx, st.Description)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"failed to get subtask description: %w\", err)\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(sb, \"  Description: %s\\n\", description)\n\t\t\t}\n\t\t\tif st.Result != \"\" {\n\t\t\t\tresult, err := t.getResultText(ctx, st.Result)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"failed to get subtask result: %w\", err)\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(sb, \"  Result: %s\\n\", result)\n\t\t\t}\n\t\t}\n\t}\n\n\tsubtasksList := sb.String()\n\tif t.summarizer != nil && len(subtasksList) > subtasksListLimit {\n\t\tsubtasksList, err = t.summarizer(ctx, truncateText(subtasksList, summarizationLimit))\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to summarize subtasks list: %w\", err)\n\t\t}\n\t}\n\n\treturn subtasksList, nil\n}\n\nfunc (t *flowStatusTool) buildRunningInfo(ctx context.Context, verbose bool) (string, error) {","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/flow_manager.go#L230-L266","documentation":"Returned by flowStatusTool.buildSubtasksList when rendering a subtask's Result in verbose mode fails. The Result field is passed to t.getResultText, which either truncates it or, when the result exceeds 2*resultLimit and a summarizer is configured, sends the (truncated) text to the LLM summarizer. The error wraps whatever the summarizer returned; it does not indicate a DB problem.","triggerScenarios":"An agent calls the flow status tool with verbose=true while at least one subtask has a non-empty Result longer than 2*resultLimit characters, and the configured t.summarizer callback returns an error (LLM provider unavailable, rate-limited, auth failure, context deadline exceeded, or malformed provider response).","commonSituations":"LLM API key expired or quota exhausted so summarization calls fail; summarizer LLM endpoint unreachable or timing out; very large subtask results (long tool outputs) pushing every result into the summarization path; ctx cancelled by an upstream timeout while the summarizer call is in flight.","solutions":["Check the wrapped cause (%w) to see if it is an LLM provider auth/quota error and fix the provider credentials or billing.","Verify the summarizer provider endpoint is reachable and increase its timeout.","Retry the tool call; summarizer failures are often transient (rate limits).","As a workaround, run the status tool with verbose=false so Result fields are not processed, or shrink subtask results so they fall under 2*resultLimit and skip summarization."],"exampleFix":"// before\ncallStatusTool(ctx, FlowStatusModeAll, true) // verbose=true forces result summarization\n// after\nif err := callStatusTool(ctx, FlowStatusModeAll, true); err != nil {\n    log.Warn(\"verbose status failed, falling back\", \"err\", err)\n    out, err = callStatusTool(ctx, FlowStatusModeAll, false) // skip result summarization\n}","handlingStrategy":"try-catch","validationCode":"// Go: preflight the summarizer before invoking the status tool\nfunc summarizerHealthy(ctx context.Context, s func(context.Context, string) (string, error)) error {\n    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)\n    defer cancel()\n    _, err := s(ctx, \"ping\")\n    return err\n}","typeGuard":"// Go: only take the summarization path when input actually needs it\nfunc needsSummarization(s string, limit int) bool {\n    return len(s) > 2*limit\n}\n// treat a nil or failing summarizer as \"use truncation\" instead of failing","tryCatchPattern":"out, err := callFlowStatusTool(ctx, mode, true)\nif err != nil {\n    var summarizerErr *SomeLLMProviderError\n    if errors.As(err, &summarizerErr) || strings.Contains(err.Error(), \"summarize\") {\n        log.Warn(\"summarization failed, retrying non-verbose\", \"err\", err)\n        out, err = callFlowStatusTool(ctx, mode, false)\n    }\n    if err != nil {\n        return fmt.Errorf(\"flow status unavailable: %w\", err)\n    }\n}","preventionTips":["Keep summarizer LLM credentials and quotas valid; alert on provider 401/429 responses.","Set a generous but bounded timeout on the summarizer context.","Keep subtask results short so they stay below 2*resultLimit and never enter the summarization path.","Have a truncation-only fallback when the summarizer is nil or failing."],"tags":["go","llm","summarization","flow-status"],"backgroundTag":"llm-summarizer-call-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}