diff --git a/apps/web/src/app/api/agents/actions/route.ts b/apps/web/src/app/api/agents/actions/route.ts index ddcdbf1d0..4d12daeca 100644 --- a/apps/web/src/app/api/agents/actions/route.ts +++ b/apps/web/src/app/api/agents/actions/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from 'next/server'; +import { formatApiError } from '@/lib/error-handling'; import { runActionAgent } from '@/lib/action-agent'; import { AVAILABLE_TOOL_NAMES } from '@/lib/action-agent'; import { hasGeminiKey } from '@/lib/gemini-client'; @@ -56,7 +57,7 @@ export async function POST(request: Request): Promise { }); } catch (error) { console.error('Action agent error:', error); - const message = error instanceof Error ? error.message : String(error); + const message = formatApiError(error).message; // Only the agent's own validation/config guards are client errors (400). // Match exact phrases so an upstream provider error that merely mentions diff --git a/apps/web/src/app/api/extract-events/route.ts b/apps/web/src/app/api/extract-events/route.ts index 8eb8f8b56..7bba4386d 100644 --- a/apps/web/src/app/api/extract-events/route.ts +++ b/apps/web/src/app/api/extract-events/route.ts @@ -1,4 +1,5 @@ import OpenAI from 'openai'; +import { formatApiError } from '@/lib/error-handling'; import { Type } from '@google/genai'; import { NextResponse } from 'next/server'; import { getGeminiClient, hasGeminiKey } from '@/lib/gemini-client'; @@ -255,7 +256,7 @@ Respond with ONLY valid JSON matching the required structure.`; return NextResponse.json({ success: true, provider, data: parsed }); } catch (error) { console.error('Event extraction error:', error); - const message = error instanceof Error ? error.message : String(error); + const message = formatApiError(error).message; return NextResponse.json({ success: false, diff --git a/apps/web/src/app/api/jobs/[jobId]/route.ts b/apps/web/src/app/api/jobs/[jobId]/route.ts index c9da65176..adfd0eb28 100644 --- a/apps/web/src/app/api/jobs/[jobId]/route.ts +++ b/apps/web/src/app/api/jobs/[jobId]/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from 'next/server'; +import { formatApiError } from '@/lib/error-handling'; import { backendHeaders } from '@/lib/pipeline-backend'; const rawBackendUrl = process.env.BACKEND_URL || ''; @@ -39,8 +40,8 @@ export async function GET( }); } catch (error) { return NextResponse.json( - { error: error instanceof Error ? error.message : String(error) }, + { error: formatApiError(error).message }, { status: 502 }, ); } -} \ No newline at end of file +} diff --git a/apps/web/src/app/api/search/route.ts b/apps/web/src/app/api/search/route.ts index 96d40b62d..0ce7646e7 100644 --- a/apps/web/src/app/api/search/route.ts +++ b/apps/web/src/app/api/search/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; +import { formatApiError } from '@/lib/error-handling'; import { getSearchIndex, resolveSearchIndexName } from '@/lib/upstash-search'; import type { SearchDocument } from '@/lib/upstash-search'; @@ -44,7 +45,7 @@ export async function POST(req: NextRequest) { } catch (error) { console.error('Upstash search error:', error); return NextResponse.json( - { error: 'search_failed', detail: error instanceof Error ? error.message : String(error) }, + { error: 'search_failed', detail: formatApiError(error).message }, { status: 502 }, ); } @@ -108,7 +109,7 @@ export async function PUT(req: NextRequest) { } catch (error) { console.error('Upstash upsert error:', error); return NextResponse.json( - { error: 'upsert_failed', detail: error instanceof Error ? error.message : String(error) }, + { error: 'upsert_failed', detail: formatApiError(error).message }, { status: 502 }, ); } diff --git a/apps/web/src/lib/gemini-client.ts b/apps/web/src/lib/gemini-client.ts index 61c977abc..8fc5f31df 100644 --- a/apps/web/src/lib/gemini-client.ts +++ b/apps/web/src/lib/gemini-client.ts @@ -1,4 +1,5 @@ import 'server-only'; +import { formatApiError } from '@/lib/error-handling'; /** * Shared Gemini/Google AI client factory. @@ -97,7 +98,7 @@ export function getGeminiRoutingLabel(): string { * Map provider errors to stable codes for API responses and logs. */ export function classifyGeminiError(error: unknown): ClassifiedGeminiError { - const message = error instanceof Error ? error.message : String(error); + const message = formatApiError(error).message; if (message.includes('BILLING_DISABLED')) { return { @@ -170,4 +171,4 @@ export function getGeminiClient(): GoogleGenAI { _lastMode = mode; } return _gemini; -} \ No newline at end of file +} diff --git a/apps/web/src/lib/gemini-video-analyzer.ts b/apps/web/src/lib/gemini-video-analyzer.ts index cfdb2746f..4744b322d 100644 --- a/apps/web/src/lib/gemini-video-analyzer.ts +++ b/apps/web/src/lib/gemini-video-analyzer.ts @@ -1,4 +1,5 @@ import 'server-only'; +import { formatApiError } from '@/lib/error-handling'; /** * Agentic Video Intelligence Engine — Gemini + Google Search grounding. @@ -297,7 +298,7 @@ export async function analyzeVideoWithGemini( const resultText = response.text || '{}'; return parseAnalysisResult(resultText); } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = formatApiError(error).message; const retryable = error instanceof AnalysisParseError || errorMessage.includes('503') || diff --git a/apps/web/src/lib/pipeline-backend-health.ts b/apps/web/src/lib/pipeline-backend-health.ts index cfd4aa42d..113e4e43a 100644 --- a/apps/web/src/lib/pipeline-backend-health.ts +++ b/apps/web/src/lib/pipeline-backend-health.ts @@ -1,4 +1,5 @@ import 'server-only'; +import { formatApiError } from '@/lib/error-handling'; import { backendHeaders } from '@/lib/pipeline-backend'; @@ -63,7 +64,7 @@ export async function checkBackendHealth( configured: true, available: false, host: backendHost(url), - reason: error instanceof Error ? error.message : String(error), + reason: formatApiError(error).message, }; } } @@ -80,4 +81,4 @@ export async function parseBackendJson(response: Response): Promise } catch { return null; } -} \ No newline at end of file +} diff --git a/apps/web/src/lib/transcription-service-improved.ts b/apps/web/src/lib/transcription-service-improved.ts index 0b933df37..7b29f0fee 100644 --- a/apps/web/src/lib/transcription-service-improved.ts +++ b/apps/web/src/lib/transcription-service-improved.ts @@ -1,4 +1,5 @@ import 'server-only'; +import { formatApiError } from '@/lib/error-handling'; import OpenAI from 'openai'; import { fetchYouTubeMetadata, formatMetadataAsContext } from '@/lib/youtube-metadata'; @@ -125,7 +126,7 @@ export async function fetchTranscript({ } catch (error) { console.warn( 'Backend transcript unavailable:', - error instanceof Error ? error.message : String(error) + formatApiError(error).message ); // Continue to fallback strategies } @@ -143,7 +144,7 @@ export async function fetchTranscript({ } catch (error) { console.warn( 'YouTube metadata fetch failed:', - error instanceof Error ? error.message : String(error) + formatApiError(error).message ); // Continue without metadata } @@ -207,7 +208,7 @@ INSTRUCTIONS: }; } } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); + const errorMsg = formatApiError(error).message; console.warn('Gemini transcription failed:', errorMsg); // Handle specific errors @@ -262,7 +263,7 @@ Be thorough — capture all key points, quotes, technical details, and chapter b }; } } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); + const errorMsg = formatApiError(error).message; console.warn('OpenAI transcription failed:', errorMsg); if (errorMsg.includes('429')) {