mirror of
https://github.com/coleam00/Archon.git
synced 2026-01-01 12:18:41 -05:00
feat: Implement comprehensive OpenAI error handling for Issue #362
Replace silent failures with clear, actionable error messages to eliminate 90-minute debugging sessions when OpenAI API quota is exhausted. ## Backend Enhancements - Add error sanitization preventing sensitive data exposure (API keys, URLs, tokens) - Add upfront API key validation before expensive operations (crawl, upload, refresh) - Implement fail-fast pattern in RAG service (no more empty results for API failures) - Add specific error handling for quota, rate limit, auth, and API errors - Add EmbeddingAuthenticationError exception with masked key prefix support ## Frontend Enhancements - Create enhanced error utilities with OpenAI-specific parsing - Build TanStack Query compatible API wrapper preserving ETag caching - Update knowledge service to use enhanced error handling - Enhance TanStack Query hooks with user-friendly error messages ## Security Features - Comprehensive regex sanitization (8 patterns) with ReDoS protection - Input validation and circular reference detection - Generic fallback messages for sensitive keywords - Bounded quantifiers to prevent regex DoS attacks ## User Experience - Clear error messages: "OpenAI API quota exhausted" - Actionable guidance: "Check your OpenAI billing dashboard and add credits" - Immediate error visibility (no more silent failures) - Appropriate error severity styling ## Architecture Compatibility - Full TanStack Query integration maintained - ETag caching and optimistic updates preserved - No performance regression (all existing tests pass) - Compatible with existing knowledge base architecture Resolves #362: Users no longer experience mysterious empty RAG results that require extensive debugging to identify OpenAI quota issues. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Enhanced API client for knowledge base operations with OpenAI error handling
|
||||
* Built on top of the ETag-aware API client with additional error parsing
|
||||
*/
|
||||
|
||||
import { callAPIWithETag } from "../../projects/shared/apiWithEtag";
|
||||
import { parseKnowledgeBaseError, type EnhancedError } from "../utils/errorHandler";
|
||||
|
||||
/**
|
||||
* API call wrapper with enhanced OpenAI error handling
|
||||
* Uses ETag caching for efficiency while adding specialized error parsing
|
||||
*/
|
||||
export async function callKnowledgeAPI<T>(
|
||||
endpoint: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
try {
|
||||
// Use the ETag-aware API client for caching benefits
|
||||
return await callAPIWithETag<T>(endpoint, options);
|
||||
} catch (error: any) {
|
||||
// Apply enhanced error parsing for OpenAI errors
|
||||
const enhancedError = parseKnowledgeBaseError({
|
||||
status: error.statusCode || error.status,
|
||||
error: error.message || error.detail || error,
|
||||
detail: error.detail
|
||||
});
|
||||
|
||||
// Preserve the original error structure but enhance with our parsing
|
||||
const finalError = error as EnhancedError;
|
||||
finalError.isOpenAIError = enhancedError.isOpenAIError;
|
||||
finalError.errorDetails = enhancedError.errorDetails;
|
||||
finalError.message = enhancedError.message;
|
||||
|
||||
throw finalError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced upload wrapper that handles FormData and file uploads with better error handling
|
||||
*/
|
||||
export async function uploadWithEnhancedErrors(
|
||||
endpoint: string,
|
||||
formData: FormData,
|
||||
timeoutMs: number = 30000
|
||||
): Promise<any> {
|
||||
const API_BASE_URL = "/api"; // Use same base as other services
|
||||
|
||||
let fullUrl = `${API_BASE_URL}${endpoint}`;
|
||||
|
||||
// Handle test environment URLs
|
||||
if (typeof process !== "undefined" && process.env?.NODE_ENV === "test") {
|
||||
const testHost = process.env?.VITE_HOST || "localhost";
|
||||
const testPort = process.env?.ARCHON_SERVER_PORT || "8181";
|
||||
fullUrl = `http://${testHost}:${testPort}${fullUrl}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(fullUrl, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
signal: AbortSignal.timeout(timeoutMs),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let errorData;
|
||||
try {
|
||||
errorData = await response.json();
|
||||
} catch {
|
||||
const text = await response.text();
|
||||
errorData = { status: response.status, error: text };
|
||||
}
|
||||
|
||||
// Apply enhanced error parsing
|
||||
const enhancedError = parseKnowledgeBaseError({
|
||||
status: response.status,
|
||||
error: errorData.detail || errorData.error || errorData,
|
||||
detail: errorData.detail
|
||||
});
|
||||
|
||||
throw enhancedError;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
} catch (error: any) {
|
||||
// Check if it's a timeout error
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
const timeoutError = parseKnowledgeBaseError(new Error('Request timed out'));
|
||||
throw timeoutError;
|
||||
}
|
||||
|
||||
// If it's already an enhanced error, re-throw it
|
||||
if (error && typeof error === 'object' && 'isOpenAIError' in error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Parse other errors through the error handler for consistency
|
||||
throw parseKnowledgeBaseError(error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user