Skip to content
Closed
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
96c2875
Fix OpenAI quota exhaustion error handling in RAG queries
leex279 Aug 19, 2025
2c16ed9
Remove unused variable to fix linting
leex279 Aug 19, 2025
f23faea
Add comprehensive frontend error handling for OpenAI API errors
leex279 Aug 19, 2025
c3a9548
Fix #362: Enhance OpenAI API error handling with sanitization and ret…
leex279 Aug 20, 2025
6652873
Implement code review findings: Security and performance improvements
leex279 Aug 20, 2025
3c0fc55
Fix OpenAI API key cache invalidation regression
leex279 Aug 21, 2025
5fbe8fb
Merge latest main into bugfix-issue-362 - Keep OpenAI error handling …
leex279 Aug 21, 2025
0a8bea6
Add comprehensive OpenAI AuthenticationError handling
leex279 Aug 21, 2025
f770675
Fix crawl success reporting when embeddings fail
leex279 Aug 21, 2025
c3cf1ee
RAG: surface OpenAI auth errors as 401; fix single-vector embedding c…
Aug 29, 2025
2c3b8ef
PR #521: address CodeRabbit review β€” consistent 401 payload; mask key…
Aug 29, 2025
46a98da
Merge PR 521: Fix authentication error handling in RAG
leex279 Aug 30, 2025
0fce8a5
Add upfront OpenAI API key validation to prevent wasted crawling
leex279 Aug 30, 2025
3337b90
Fix UI error display: restore enhanced error handling in KnowledgeBas…
leex279 Aug 30, 2025
de8aa8a
fix: Address CodeRabbit review comments
leex279 Aug 31, 2025
2ffe1ac
Merge latest main changes into OpenAI error handling branch
leex279 Aug 31, 2025
26d0588
fix: Improve UI error display for OpenAI authentication errors
leex279 Aug 31, 2025
afe8c56
fix: Add clear 401 status code to authentication error message
leex279 Aug 31, 2025
39451b7
debug: Add detailed logging to diagnose error parsing issue
leex279 Aug 31, 2025
071840a
fix: Use enhanced UI error messages directly in error parsing
leex279 Aug 31, 2025
71e83dd
fix: Address CodeRabbit review suggestions for error handling
leex279 Aug 31, 2025
3e09eba
fix: Resolve backend test failures
leex279 Aug 31, 2025
99375c6
fix: Update tests for fail-fast behavior and fix storage mocks
leex279 Aug 31, 2025
1f382e5
fix: Add comprehensive mocking to prevent real API calls in tests
leex279 Aug 31, 2025
7c8d3e4
fix: Correct chunk count in document storage test
leex279 Aug 31, 2025
5ef7c2f
fix: Remove non-existent EmbeddingResult import causing test collecti…
leex279 Aug 31, 2025
e1aec98
fix: Update tests to expect masked API key prefix
leex279 Aug 31, 2025
65c4363
fix: Allow test-specific mocking by removing autouse create_embedding…
leex279 Aug 31, 2025
9cc7023
fix: Mock search_documents directly to test error handling pipeline
leex279 Aug 31, 2025
efeaaab
Delete OPENAI_ERROR_HANDLING.md
leex279 Aug 31, 2025
7a3775a
fix: Address critical and warning issues from code review
leex279 Aug 31, 2025
ff16882
Merge origin/main into bugfix-issue-362
leex279 Sep 6, 2025
e5235e6
Fix missing import for enhanced error handling in KnowledgeBasePage
leex279 Sep 6, 2025
57ee971
Add debug logging to frontend error parsing
leex279 Sep 6, 2025
5562605
Merge remote-tracking branch 'origin/main' into bugfix-issue-362
leex279 Sep 6, 2025
63c51e3
fix: Address ReDoS vulnerability in regex patterns
leex279 Sep 6, 2025
80e0cff
fix: Optimize isSafeObject performance in error handler
leex279 Sep 6, 2025
faf5ece
fix: Add frontend sanitization for sensitive data in error messages
leex279 Sep 6, 2025
9c85295
fix: Sanitize string error messages to prevent sensitive data leakage
leex279 Sep 6, 2025
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
30 changes: 30 additions & 0 deletions archon-ui-main/src/components/mcp/ToolTestingPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { X, Play, ChevronDown, TerminalSquare, Copy, Check, MinusCircle, Maximiz
import { Client, Tool } from './MCPClients';
import { Button } from '../ui/Button';
import { mcpClientService } from '../../services/mcpClientService';
import { ErrorAlert, useErrorHandler } from '../ui/ErrorAlert';
import { parseKnowledgeBaseError, EnhancedError } from '../../services/knowledgeBaseErrorHandler';

interface ToolTestingPanelProps {
client: Client | null;
Expand Down Expand Up @@ -37,6 +39,7 @@ export const ToolTestingPanel = ({
const [isResizing, setIsResizing] = useState(false);
const [isMaximized, setIsMaximized] = useState(false);
const [isExecuting, setIsExecuting] = useState(false);
const { error, setError, clearError } = useErrorHandler();
const terminalRef = useRef<HTMLDivElement>(null);
const resizeHandleRef = useRef<HTMLDivElement>(null);
const panelRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -92,6 +95,13 @@ export const ToolTestingPanel = ({
};
}, [isResizing]);

// Cleanup error state on component unmount
useEffect(() => {
return () => {
clearError(); // Clear any pending errors when component unmounts
};
}, [clearError]);

// Handle tool selection
const handleToolSelect = (tool: Tool) => {
setSelectedTool(tool);
Expand Down Expand Up @@ -260,9 +270,23 @@ export const ToolTestingPanel = ({

} catch (error: any) {
console.error('MCP tool execution failed:', error);

// Parse error through enhanced error handler for better user experience
const enhancedError = parseKnowledgeBaseError(error);
setError(enhancedError);

setTimeout(() => {
addTypingLine(`> ERROR: Failed to execute tool on ${client.name}`, false, true);
addTypingLine(`> ${error.message || 'Unknown error occurred'}`, false, true);

// Add special handling for OpenAI/RAG related errors
if (enhancedError.isOpenAIError && enhancedError.errorDetails) {
addTypingLine(`> Error Type: ${enhancedError.errorDetails.error_type}`, false, true);
if (enhancedError.errorDetails.tokens_used) {
addTypingLine(`> Tokens Used: ${enhancedError.errorDetails.tokens_used}`, false, true);
}
}

addTypingLine('> Execution failed');
setIsExecuting(false);
}, 300);
Expand All @@ -286,6 +310,9 @@ export const ToolTestingPanel = ({
const executeSelectedTool = () => {
if (!selectedTool || !client || isExecuting) return;

// Clear any previous errors
clearError();

// Validate required parameters
const validationError = validateParameters();
if (validationError) {
Expand Down Expand Up @@ -415,6 +442,9 @@ export const ToolTestingPanel = ({

{/* Content */}
<div className="px-6 py-4 h-[calc(100%-73px)] overflow-y-auto">
{/* Error Alert for OpenAI/RAG errors */}
<ErrorAlert error={error} onDismiss={clearError} />

{client.tools.length === 0 ? (
<div className="flex items-center justify-center h-full">
<div className="text-center">
Expand Down
46 changes: 46 additions & 0 deletions archon-ui-main/src/components/ui/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Basic Alert component for error display
*
* Provides consistent styling for alerts throughout the application.
*/

import React from 'react';

interface AlertProps {
children: React.ReactNode;
variant?: 'default' | 'destructive';
className?: string;
}

export const Alert: React.FC<AlertProps> = ({
children,
variant = 'default',
className = ''
}) => {
const baseClasses = 'p-4 border rounded-lg';
const variantClasses = variant === 'destructive'
? 'border-red-300 bg-red-50 text-red-800'
: 'border-blue-300 bg-blue-50 text-blue-800';

return (
<div className={`${baseClasses} ${variantClasses} ${className}`}>
{children}
</div>
);
};

interface AlertDescriptionProps {
children: React.ReactNode;
className?: string;
}

export const AlertDescription: React.FC<AlertDescriptionProps> = ({
children,
className = ''
}) => {
return (
<div className={className}>
{children}
</div>
);
};
162 changes: 162 additions & 0 deletions archon-ui-main/src/components/ui/ErrorAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* Enhanced error alert component for displaying OpenAI API errors
*
* Provides specialized UI feedback for different error types including
* quota exhaustion, rate limiting, and API errors.
*
* Related to GitHub issue #362 - improves user experience by showing
* clear, actionable error messages instead of generic failures.
*/

import React from 'react';
import { Alert, AlertDescription } from './Alert';
import { EnhancedError, getDisplayErrorMessage, getErrorSeverity, getErrorAction } from '../../services/knowledgeBaseErrorHandler';

interface ErrorAlertProps {
error: EnhancedError | null;
onDismiss?: () => void;
className?: string;
}

/**
* Validate error object structure and properties
*/
function isValidError(error: any): error is EnhancedError {
if (!error || typeof error !== 'object') return false;

// Check that it has basic Error properties
if (typeof error.message !== 'string') return false;

// If it claims to be an OpenAI error, validate the structure
if (error.isOpenAIError === true) {
if (!error.errorDetails || typeof error.errorDetails !== 'object') return false;

const { error_type, message, error: errorField } = error.errorDetails;
if (typeof error_type !== 'string' || typeof message !== 'string') return false;
}

// If statusCode is present, it should be a number
if (error.statusCode !== undefined && typeof error.statusCode !== 'number') return false;

return true;
}

export const ErrorAlert: React.FC<ErrorAlertProps> = ({ error, onDismiss, className = '' }) => {
if (!error) return null;

// Validate error object structure
if (!isValidError(error)) {
console.warn('Invalid error object passed to ErrorAlert:', error);
// Create a fallback error object
const fallbackError: EnhancedError = Object.assign(new Error('Invalid error object received'), {
errorDetails: {
error: 'validation_error',
message: 'An error occurred but the error details could not be parsed properly.',
error_type: 'api_error' as const
}
});
error = fallbackError;
}

const severity = getErrorSeverity(error);
const displayMessage = getDisplayErrorMessage(error);
const suggestedAction = getErrorAction(error);

// Determine alert styling based on severity
const alertVariant = severity === 'error' ? 'destructive' : 'default';

// Special styling for OpenAI errors
const isOpenAIError = error.isOpenAIError;

return (
<Alert variant={alertVariant} className={`mb-4 ${className}`}>
<div className="flex items-start justify-between">
<div className="flex-1">
{/* Error icon based on type */}
<div className="flex items-center gap-2 mb-2">
{isOpenAIError ? (
<span className="text-lg">⚠️</span>
) : (
<span className="text-lg">❌</span>
)}
<span className="font-semibold">
{isOpenAIError ? 'OpenAI API Error' : 'Error'}
</span>
</div>

{/* Main error message */}
<AlertDescription className="text-sm mb-2">
{displayMessage}
</AlertDescription>

{/* Suggested action for OpenAI errors */}
{suggestedAction && (
<div className="mt-2 p-2 bg-blue-50 border-l-4 border-blue-400 rounded-r">
<p className="text-sm font-medium text-blue-800">
πŸ’‘ Suggested action:
</p>
<p className="text-sm text-blue-700 mt-1">
{suggestedAction}
</p>
</div>
)}

{/* Token usage info for quota errors */}
{error.errorDetails?.tokens_used && (
<div className="mt-2 text-xs text-gray-600">
Tokens used: {error.errorDetails.tokens_used.toLocaleString()}
</div>
)}
</div>

{/* Dismiss button */}
{onDismiss && (
<button
onClick={onDismiss}
className="ml-4 text-gray-400 hover:text-gray-600 focus:outline-none"
aria-label="Dismiss error"
>
<span className="text-lg">Γ—</span>
</button>
)}
</div>
</Alert>
);
};

/**
* Hook for handling knowledge base operation errors
*
* Usage example:
* ```tsx
* const { error, setError, clearError } = useErrorHandler();
*
* const handleSearch = async () => {
* try {
* await knowledgeBaseService.searchKnowledgeBase(query);
* } catch (err) {
* setError(err as EnhancedError);
* }
* };
*
* return (
* <>
* <ErrorAlert error={error} onDismiss={clearError} />
* <YourComponentContent />
* </>
* );
* ```
*/
export function useErrorHandler() {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const [error, setError] = React.useState<EnhancedError | null>(null);

const clearError = React.useCallback(() => {
setError(null);
}, []);

return {
error,
setError,
clearError
};
}
44 changes: 40 additions & 4 deletions archon-ui-main/src/pages/KnowledgeBasePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { KnowledgeItemCard } from '../components/knowledge-base/KnowledgeItemCar
import { GroupedKnowledgeItemCard } from '../components/knowledge-base/GroupedKnowledgeItemCard';
import { KnowledgeGridSkeleton, KnowledgeTableSkeleton } from '../components/knowledge-base/KnowledgeItemSkeleton';
import { GroupCreationModal } from '../components/knowledge-base/GroupCreationModal';
import { parseKnowledgeBaseError, getDisplayErrorMessage, getErrorAction, EnhancedError } from '../services/knowledgeBaseErrorHandler';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Map 401/auth errors to actionable UI + small import nit

  • Ensure getDisplayErrorMessage/getErrorAction handle OpenAI auth failures explicitly (401 and error_type like authentication/auth_error), per PR goals. Right now handler snippets only cover 429/502/503 and quota_exhausted/rate_limit/api_error.
  • Minor: EnhancedError import is unused here; either remove or type enhancedError.

Example adjustments in knowledgeBaseErrorHandler.ts (outside this file):

// getDisplayErrorMessage
case 'authentication':
case 'auth_error':
case 'invalid_api_key':
  return 'OpenAI authentication failed. Please verify your API key or organization/project configuration.';

// HTTP codes:
case 401:
  return 'Authentication failed (401). Check your API key configuration.';

// getErrorAction
case 'authentication':
case 'auth_error':
case 'invalid_api_key':
  return 'Verify your OpenAI API key in Settings';


// Fallback for HTTP 401
if (error.statusCode === 401) return 'Verify your OpenAI API key in Settings';
πŸ€– Prompt for AI Agents
In archon-ui-main/src/pages/KnowledgeBasePage.tsx around line 22, the import
brings in EnhancedError which is unused and the knowledgeBaseErrorHandler needs
to explicitly map OpenAI authentication failures; remove the unused
EnhancedError import (or use it to type enhancedError) and update the handler
functions in services/knowledgeBaseErrorHandler.ts to add cases for error types
'authentication', 'auth_error', and 'invalid_api_key' returning an
authentication-specific display message and action, add an HTTP 401 branch
returning an authentication message in getDisplayErrorMessage, and add a
fallback check that if error.statusCode === 401 getErrorAction returns 'Verify
your OpenAI API key in Settings' so UI shows an actionable prompt for auth
failures.


const extractDomain = (url: string): string => {
try {
Expand Down Expand Up @@ -92,7 +93,11 @@ export const KnowledgeBasePage = () => {
setTotalItems(response.total);
} catch (error) {
console.error('Failed to load knowledge items:', error);
showToast('Failed to load knowledge items', 'error');

// Parse the error using enhanced error handler
const enhancedError = parseKnowledgeBaseError(error);
const displayMessage = getDisplayErrorMessage(enhancedError);
showToast(displayMessage, 'error');
setKnowledgeItems([]);
} finally {
setLoading(false);
Expand Down Expand Up @@ -498,7 +503,17 @@ export const KnowledgeBasePage = () => {
}
} catch (error) {
console.error('Failed to refresh knowledge item:', error);
showToast('Failed to refresh knowledge item', 'error');

// Parse the error using enhanced error handler
const enhancedError = parseKnowledgeBaseError(error);
const displayMessage = getDisplayErrorMessage(enhancedError);
const suggestedAction = getErrorAction(enhancedError);

const fullMessage = suggestedAction
? `${displayMessage} ${suggestedAction}`
: displayMessage;

showToast(fullMessage, 'error');
}
};

Expand Down Expand Up @@ -737,7 +752,17 @@ export const KnowledgeBasePage = () => {
}
} catch (error) {
console.error('Failed to retry:', error);
showToast(`Retry failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 'error');

// Parse the error using enhanced error handler
const enhancedError = parseKnowledgeBaseError(error);
const displayMessage = getDisplayErrorMessage(enhancedError);
const suggestedAction = getErrorAction(enhancedError);

const fullMessage = suggestedAction
? `Retry failed: ${displayMessage} ${suggestedAction}`
: `Retry failed: ${displayMessage}`;

showToast(fullMessage, 'error');
}
};

Expand Down Expand Up @@ -1384,7 +1409,18 @@ const AddKnowledgeModal = ({
}
} catch (error) {
console.error('Failed to add knowledge:', error);
showToast('Failed to add knowledge source', 'error');

// Parse the error using the enhanced error handler
const enhancedError = parseKnowledgeBaseError(error);
const displayMessage = getDisplayErrorMessage(enhancedError);
const suggestedAction = getErrorAction(enhancedError);

// Show enhanced error message with action if available
const fullMessage = suggestedAction
? `${displayMessage} ${suggestedAction}`
: displayMessage;

showToast(fullMessage, 'error');
} finally {
setLoading(false);
}
Expand Down
Loading