From 812df963779fdf3011befc8ebfe7f4a157f605cc Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 21 Dec 2025 22:45:29 +0000 Subject: [PATCH] feat: surface AI patrol errors as findings When AI patrol fails due to API issues like insufficient balance, invalid API key, or rate limiting, we now create a finding that appears in the AI Insights tab. This makes the issue visible to users rather than hidden in logs. The finding includes: - Clear description of the issue (e.g., 'Insufficient API credits') - Recommendation for how to fix it - Evidence showing the actual error message --- internal/ai/patrol.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/internal/ai/patrol.go b/internal/ai/patrol.go index 81aaa6b..b363c50 100644 --- a/internal/ai/patrol.go +++ b/internal/ai/patrol.go @@ -777,6 +777,44 @@ func (p *PatrolService) runPatrol(ctx context.Context) { if aiErr != nil { log.Warn().Err(aiErr).Msg("AI Patrol: LLM analysis failed") runStats.errors++ + + // Create a finding to surface this error to the user + errMsg := aiErr.Error() + var title, description, recommendation string + if strings.Contains(errMsg, "Insufficient Balance") || strings.Contains(errMsg, "402") { + title = "AI Patrol: Insufficient API credits" + description = "The AI patrol cannot analyze your infrastructure because your AI provider account has insufficient credits." + recommendation = "Add credits to your AI provider account (DeepSeek, OpenAI, etc.) or switch to a different provider in AI Settings." + } else if strings.Contains(errMsg, "401") || strings.Contains(errMsg, "Unauthorized") { + title = "AI Patrol: Invalid API key" + description = "The AI patrol cannot analyze your infrastructure because the API key is invalid or expired." + recommendation = "Check your API key in AI Settings and verify it is correct." + } else if strings.Contains(errMsg, "rate limit") || strings.Contains(errMsg, "429") { + title = "AI Patrol: Rate limited" + description = "The AI patrol is being rate limited by your AI provider. Analysis will be retried on the next patrol run." + recommendation = "Wait for the rate limit to reset, or consider upgrading your API plan for higher limits." + } else { + title = "AI Patrol: Analysis failed" + description = fmt.Sprintf("The AI patrol encountered an error while analyzing your infrastructure: %s", errMsg) + recommendation = "Check your AI settings and API key. If the problem persists, check the logs for more details." + } + + errorFinding := &Finding{ + ID: generateFindingID("ai-service", "reliability", "ai-patrol-error"), + Key: "ai-patrol-error", + Severity: "warning", + Category: "reliability", + ResourceID: "ai-service", + ResourceName: "AI Patrol Service", + ResourceType: "service", + Title: title, + Description: description, + Recommendation: recommendation, + Evidence: fmt.Sprintf("Error: %s", errMsg), + DetectedAt: time.Now(), + LastSeenAt: time.Now(), + } + trackFinding(errorFinding) } else if aiResult != nil { runStats.aiAnalysis = aiResult for _, f := range aiResult.Findings {