Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
41458bb
Centralize LLM Error Handling and Fallback Messages
Amazing-Stardom Oct 2, 2026
0da1b16
Add Centralized LLM Error Categorization and Action Cards
Amazing-Stardom Oct 3, 2026
336cf44
Clean Up WebChat Logging and Refactor Chat UI Layout
Amazing-Stardom Oct 3, 2026
8b9b2b4
Add Raw LLM Error Tracking to Debug Artifacts
Amazing-Stardom Oct 3, 2026
e2a8871
Update Error Identification and Layout in Chat Conversation
Amazing-Stardom Oct 3, 2026
78cf87d
Refactor LLM Classification Error Handling
Amazing-Stardom Oct 3, 2026
8642a73
Update LLM Error Handling and UI Styles
Amazing-Stardom Oct 3, 2026
b91a111
Refactor Error Handling and Timeouts in AI and Chat Services
Amazing-Stardom Oct 3, 2026
e5fbe4b
Parse Slideshow Text With DOMParser
Amazing-Stardom Oct 3, 2026
ee58b87
Update Dependency Version Constraints for urllib3 and PyJWT
Amazing-Stardom Oct 3, 2026
8f1589c
Centralize LLM Error Helpers and Secure Chat Navigation
Amazing-Stardom Oct 3, 2026
dc07419
Add Structured Error Handling and UI Recovery for MCP Failures
Amazing-Stardom Oct 7, 2026
8b81fbc
Centralize MCP Error Handling and Refactor Chat Action Card
Amazing-Stardom Oct 7, 2026
5d92407
Standardize Error Responses as Blockquotes in MCP UI
Amazing-Stardom Oct 9, 2026
960ee3c
Fix LLM Error Classification and Chat UI Stability
Amazing-Stardom Oct 9, 2026
7de4779
Refactor AI Connector Error Categorization
Amazing-Stardom Oct 9, 2026
44a2939
Refactor Error Handling and Optimize Chat Payload Processing
Amazing-Stardom Oct 9, 2026
43fad89
Optimize Plain Text Extraction and Chat Message Parsing
Amazing-Stardom Oct 9, 2026
9624ad4
Suppress DOMParser Security Warning in Slideshow Parser
Amazing-Stardom Oct 9, 2026
a941676
Refactor History Slice Access
Amazing-Stardom Oct 9, 2026
51bc0ef
Update Chat History Extraction and External Link Navigation
Amazing-Stardom Oct 9, 2026
a6eec85
Refactor Conversation History Parsing and HTML Text Extraction
Amazing-Stardom Oct 9, 2026
bf5d8ff
Upgrade Project Dependencies and Add Vulnerability Suppression
Amazing-Stardom Oct 9, 2026
9f1bb9a
Refactor Slice Bounds and HTML Entity Decoding
Amazing-Stardom Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 1 addition & 84 deletions config/osv-scanner.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,89 +3,6 @@
id = "GO-2026-5932"
reason = "False positive — golang.org/x/crypto/openpgp sub-package is never imported by this project; only golang.org/x/crypto/bcrypt is used."


# --- Accepted risks, re-check by the ignoreUntil date ---
[[ignoredVulns]]
id = "GHSA-2j4c-ffch-9f23"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-5h23-36rv-pm65"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-8jjq-8j9w-m2v6"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-8mcq-6wmr-jrjv"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-c85p-xxjj-2r75"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-fq3v-74gv-27gm"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-fw94-4wwp-w8pw"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-g27h-8qhm-6pff"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-jrfj-fhj2-jjvm"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-jw42-f3rr-4cc3"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-mx22-3794-2vpv"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-r7x5-4969-99p7"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-rxcj-4pj5-74gr"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-wp2g-vpjj-g53r"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-x2q3-8cjh-766f"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): github.com/xuri/excelize/v2 2.11.0 has no tagged release with this fix yet (only unreleased commits). Only used write-only (excelize.NewFile in internal/api/taxonomy_report_handler.go) to build the taxonomy report export; it never opens or parses uploaded spreadsheets. Upgrade to the next tagged excelize release, then remove this entry."

[[ignoredVulns]]
id = "GHSA-vfj7-8cjw-p6xm"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): braces 3.0.3 is a dev-only build dependency (ui/, extension/) and no fixed version exists yet. Not shipped in the server binary or UI bundle."

[[ignoredVulns]]
id = "GHSA-rj75-hqrm-r3gf"
ignoreUntil = 2027-01-08T00:00:00Z
reason = "Accepted risk (2026-10-10): top-level postcss-selector-parser is fixed (7.1.6); remaining 6.1.4 copies are nested under tailwindcss/postcss plugins pinned to ^6 (dev-only CSS tooling). Clears when those tools move to postcss-selector-parser 7."
reason = "braces v3.0.3 is the latest version available and there is no patch. As a devDependency only invoked at build time, it is not exposed to untrusted user input."
74 changes: 74 additions & 0 deletions docs/mcp/mcp-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# MCP & AI Provider Error Handling

The LiveReview WebChat endpoint (`/api/v1/chat/send`) has multiple layers of resilience and error handling for the AI layer and the internal MCP server.

To prevent user confusion when errors occur, the backend intercepts these failures and injects specific UI components into the response:
- **Blockquote Messages**: A stylized error message explaining what happened.
- **Action Cards**: A card with a button (e.g., "Configure AI Provider", "Try Again") directing the user on how to fix it.
- **Suggested Questions**: Helpful fallback questions (e.g., "Where do I get an AI API key?").
- **Debug Artifacts**: The raw underlying error (e.g., HTTP 502, JSON parsing errors) stored in the debug logs for developers.

## Error Handling Cases

To make it easier to debug, we categorize errors into two main areas: **Internal MCP Issues (Our End)** and **LLM Provider Issues (Model Side)**.

### 1. Internal MCP Issues (Our End)
These occur rarely, usually due to infrastructure or internal server problems. When these happen, we display a clear message indicating the problem is on our end, and provide debug logs that the user can share with us for troubleshooting.

| Case | Trigger / Condition | Message Prefix | Action Card Title | Suggested Questions | Fallback Behavior |
|------|---------------------|----------------|-------------------|---------------------|-------------------|
| **MCP Server Offline** | Connection to the MCP server fails (e.g. `502 Bad Gateway`). | `> **Something went wrong on our end**` | **MCP Server Offline** (Button: Try Again) | None | Turn aborted. Debug log shows raw network error. |

### 2. LLM Provider Issues (Model Side)
These are the most common issues. They occur due to provider rate limits, model deprecations, missing API keys, or hallucinated/truncated formatting.

| Case | Trigger / Condition | Message Prefix | Action Card Title | Suggested Questions | Fallback Behavior |
|------|---------------------|----------------|-------------------|---------------------|-------------------|
| **Auth / Config Issue** | AI Provider API key is invalid or missing (`ErrCategoryAuth`). | `> **Action Required: AI Provider Issue**` | **Configuration Required** (Button: Configure AI Provider) | `DefaultAIErrorSuggestedQuestions` | Turn aborted. |
| **Provider Overloaded** | AI Provider returns 503 or 429 (`ErrCategoryOverload`). | `> **AI Provider is Busy**` | **High Traffic Detected** (Button: Configure AI Provider) | `DefaultAIErrorSuggestedQuestions` | **Graceful Degradation**: If this happens during `classify`, the backend degrades to `product_guidance` mode, attaches the Action Card, and successfully generates a helpful product answer. |
| **Model Deprecated** | The configured AI model was removed (`ErrCategoryDeprecated`). | `> **Model No Longer Available**` | **Model No Longer Available** (Button: Configure AI Provider) | `DefaultAIErrorSuggestedQuestions` | Turn aborted. |
| **Timeout Error** | AI Provider takes too long to respond (`ErrCategoryTimeout`). | `> **Analysis Took Too Long**` | **Timeout Error** (Button: Try Again) | `DefaultAIErrorSuggestedQuestions` | Turn aborted. |
| **Unexpected AI Error** | Any other unclassified LLM API error (`ErrCategoryUnknown`). | `> **Unexpected Error**` | **Unexpected Error** (Button: Try Again) | `DefaultAIErrorSuggestedQuestions` | Turn aborted. |
| **Model Output Error** | The LLM returns truncated or malformed JSON (e.g., `envelope unmarshal failed`). | `> **Model Output Error**` | **Formatting Error** (Button: Try Again) | `DefaultAIErrorSuggestedQuestions` | Turn aborted (often caused by `maxOutputTokens` limits). |
| **No Data Found** | An analytics query successfully completes but returns 0 rows. | (Context-specific text explaining no data) | None | `DefaultNoDataSuggestedQuestions` | Chart rendering is skipped, dynamic text explaining the empty result is returned. |

## Flow Overview

1. **Early Failures**: If the MCP connection fails before the agent loop starts, `webchat_handler.go` intercepts it and constructs a `HistoryEntry` containing the `ActionCard` and `DebugArtifacts`.
2. **During the Agent Loop**: If the LLM call fails, `agent.go` categorizes the error (using `aiconnectors.CategorizeLLMError`).
- For most errors, the loop aborts and returns an error template.
- For `classify` overload errors, it intercepts the error, appends the Action Card, and forces the agent to act as a `product_guidance` agent for that turn.
3. **Output Parsing**: If the LLM succeeds but its JSON is malformed (e.g., due to token limits), `analytics.go` returns `Model Output Error` and attaches the Formatting Error action card.
4. **Persistence**: All of these states correctly persist the `action_card` and `debug_artifacts` to the `chat_messages` table via `persistReply`, so that they reload flawlessly if the user refreshes the page.

## API Response Structure

The `/api/v1/chat/send` endpoint returns a structured JSON payload (`WebChatResponse`). To support dynamic UI error rendering without hardcoded string matching on the frontend, the backend explicitly sets the `is_error` flag and provides the `action_card` payload.

```json
{
"response": "> **Something went wrong on our end**\n> \n> I am having trouble connecting to my internal data tools right now. The technical details have been attached below for troubleshooting. Please try your request again in a few moments.",
"conversationId": 123,
"sessionId": "abc-123",
"is_error": true,
"action_card": {
"title": "MCP Server Offline",
"description": "The internal data server is unreachable (502 Bad Gateway).",
"button_text": "Try Again",
"action_url": "#retry"
},
"debug_artifacts": {
"raw_llm_error": "MCP Connection Failed: 502 Bad Gateway"
},
"suggested_questions": [
{
"category": "Troubleshooting",
"questions": ["Where do I configure my AI provider?"]
}
]
}
```

By passing `is_error: true`, the React frontend (`ChatConversation.tsx`) automatically wraps the response in a red error blockquote and displays the provided `action_card`.


65 changes: 65 additions & 0 deletions docs/mcp_error_handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# MCP LLM Error Handling

When interacting with multiple AI Providers (Google, OpenAI, Anthropic) via `langchaingo`, LLM calls can fail in messy, unpredictable ways. Providers may return HTTP 503s for overload, 429s for rate limits, or 401s for revoked keys.

If we return these raw errors directly to the MCP client (the chat UI), the turn crashes and the user sees ugly stack traces (e.g., `googleapi: Error 503`).

To solve this, we use a **centralized UI fallback mechanism** that traps raw SDK errors and gracefully degrades the UI without disrupting the core ReAct loop.

## The Architecture

The error handling logic lives in `internal/mcpagent/provider.go` and consists of two functions:

### 1. `CategorizeLLMError(err error) LLMErrorCategory`
This function intercepts an error, inspects it for `langchaingo` normalizations, unwraps leaked provider-specific errors (like `*googleapi.Error`), and falls back to string matching.

It maps the messy error into one of four clean categories:
* `ErrCategoryAuth` (401, 403, 404, invalid keys, revoked models)
* `ErrCategoryOverload` (429, 503, rate limits, high demand)
* `ErrCategoryTimeout` (504, context deadline exceeded)
* `ErrCategoryUnknown` (Prompt parsing errors, network disconnects)

### 2. `GetLLMFallbackMessage(cat LLMErrorCategory) string`
This function takes a category and returns a beautifully formatted, non-emoji Markdown string to show the user.
* If it returns a non-empty string, **the turn is considered saved**. We show the user this message.
* If it returns `""` (for `ErrCategoryUnknown`), the error is fatal and should be bubbled up.

## How to use it

We deliberately **do not** wrap the original `a.provider.Complete(...)` function. We keep the core LLM signatures untouched. Instead, you just "pick up" the error immediately after the LLM call fails.

### Example (from `agent.go`)

```go
response, usage, err := a.provider.Complete(ctx, history, tools, completeOpts...)
if err != nil {
// 1. Pick up the error and categorize it
errCategory := CategorizeLLMError(err)

// 2. Ask for a clean UI message
msg := GetLLMFallbackMessage(errCategory)

// 3. If there is no clean fallback, bubble up the fatal error
if msg == "" {
return "", history, nil, nil, fmt.Errorf("llm completion failed: %w", err)
}

// 4. Otherwise, inject the fallback message as an AI response and return a nil error!
// This gracefully completes the turn and renders the message in the UI.
history = append(history, HistoryEntry{
"role": "assistant",
"content": msg,
"text": msg,
})
return msg, history, nil, nil, nil
}
```

## Adding new Fallback Scenarios

If you need to handle a new failure mode (e.g. `ContextTooLarge`):
1. Add `ErrCategoryContextSize` to the const block in `provider.go`.
2. Add the detection logic to `CategorizeLLMError`.
3. Add the exact Markdown message you want the user to see to the `switch` block in `GetLLMFallbackMessage`.

This guarantees every MCP LLM call across the app degrades consistently.
Loading
Loading