From 3b82e7d2249ff5323a3c239d943494b15b9fa544 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 28 May 2025 19:43:37 -0700 Subject: [PATCH] improvement(providers): added enhanced error logging --- apps/sim/app/api/providers/route.ts | 68 ++++++++++++++-- .../executor/handlers/agent/agent-handler.ts | 79 ++++++++++++++++++- 2 files changed, 141 insertions(+), 6 deletions(-) diff --git a/apps/sim/app/api/providers/route.ts b/apps/sim/app/api/providers/route.ts index fabf6162c7..400b9e1a21 100644 --- a/apps/sim/app/api/providers/route.ts +++ b/apps/sim/app/api/providers/route.ts @@ -12,7 +12,16 @@ export const dynamic = 'force-dynamic' * Server-side proxy for provider requests */ export async function POST(request: NextRequest) { + const requestId = crypto.randomUUID().slice(0, 8) + const startTime = Date.now() + try { + logger.info(`[${requestId}] Provider API request started`, { + timestamp: new Date().toISOString(), + userAgent: request.headers.get('User-Agent'), + contentType: request.headers.get('Content-Type'), + }) + const body = await request.json() const { provider, @@ -29,17 +38,44 @@ export async function POST(request: NextRequest) { messages, } = body + logger.info(`[${requestId}] Provider request details`, { + provider, + model, + hasSystemPrompt: !!systemPrompt, + hasContext: !!context, + hasTools: !!tools?.length, + toolCount: tools?.length || 0, + hasApiKey: !!apiKey, + hasResponseFormat: !!responseFormat, + workflowId, + stream: !!stream, + hasMessages: !!messages?.length, + messageCount: messages?.length || 0, + }) + let finalApiKey: string try { finalApiKey = getApiKey(provider, model, apiKey) } catch (error) { - logger.error('Failed to get API key:', error) + logger.error(`[${requestId}] Failed to get API key:`, { + provider, + model, + error: error instanceof Error ? error.message : String(error), + hasProvidedApiKey: !!apiKey, + }) return NextResponse.json( { error: error instanceof Error ? error.message : 'API key error' }, { status: 400 } ) } + logger.info(`[${requestId}] Executing provider request`, { + provider, + model, + workflowId, + hasApiKey: !!finalApiKey, + }) + // Execute provider request directly with the managed key const response = await executeProviderRequest(provider, { model, @@ -55,6 +91,20 @@ export async function POST(request: NextRequest) { messages, }) + const executionTime = Date.now() - startTime + logger.info(`[${requestId}] Provider request completed successfully`, { + provider, + model, + workflowId, + executionTime, + responseType: + response instanceof ReadableStream + ? 'stream' + : response && typeof response === 'object' && 'stream' in response + ? 'streaming-execution' + : 'json', + }) + // Check if the response is a StreamingExecution if ( response && @@ -63,7 +113,7 @@ export async function POST(request: NextRequest) { 'execution' in response ) { const streamingExec = response as StreamingExecution - logger.info('Received StreamingExecution from provider') + logger.info(`[${requestId}] Received StreamingExecution from provider`) // Extract the stream and execution data const stream = streamingExec.stream @@ -110,7 +160,7 @@ export async function POST(request: NextRequest) { } executionDataHeader = JSON.stringify(safeExecutionData) } catch (error) { - logger.error('Failed to serialize execution data:', error) + logger.error(`[${requestId}] Failed to serialize execution data:`, error) executionDataHeader = JSON.stringify({ success: executionData.success, error: 'Failed to serialize full execution data', @@ -130,7 +180,7 @@ export async function POST(request: NextRequest) { // Check if the response is a ReadableStream for streaming if (response instanceof ReadableStream) { - logger.info('Streaming response from provider') + logger.info(`[${requestId}] Streaming response from provider`) return new Response(response, { headers: { 'Content-Type': 'text/event-stream', @@ -143,7 +193,15 @@ export async function POST(request: NextRequest) { // Return regular JSON response for non-streaming return NextResponse.json(response) } catch (error) { - logger.error('Provider request failed:', error) + const executionTime = Date.now() - startTime + logger.error(`[${requestId}] Provider request failed:`, { + error: error instanceof Error ? error.message : String(error), + errorName: error instanceof Error ? error.name : 'Unknown', + errorStack: error instanceof Error ? error.stack : undefined, + executionTime, + timestamp: new Date().toISOString(), + }) + return NextResponse.json( { error: error instanceof Error ? error.message : String(error) }, { status: 500 } diff --git a/apps/sim/executor/handlers/agent/agent-handler.ts b/apps/sim/executor/handlers/agent/agent-handler.ts index e8961c3aeb..70984ae3d8 100644 --- a/apps/sim/executor/handlers/agent/agent-handler.ts +++ b/apps/sim/executor/handlers/agent/agent-handler.ts @@ -344,25 +344,58 @@ export class AgentBlockHandler implements BlockHandler { const url = new URL('/api/providers', baseUrl) try { + logger.info(`Making provider request to: ${url.toString()}`, { + workflowId: context.workflowId, + blockId: block.id, + provider: providerId, + model, + timestamp: new Date().toISOString(), + }) + const response = await fetch(url.toString(), { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(providerRequest), + // Add timeout and signal for better error handling + signal: AbortSignal.timeout(120000), // 2 minute timeout }) if (!response.ok) { // Try to extract a helpful error message let errorMessage = `Provider API request failed with status ${response.status}` + let errorDetails = null + try { const errorData = await response.json() if (errorData.error) { errorMessage = errorData.error + errorDetails = errorData } } catch (_e) { - // If JSON parsing fails, use the original error message + // If JSON parsing fails, try to get text response + try { + const textError = await response.text() + if (textError) { + errorDetails = { textResponse: textError } + } + } catch (_textError) { + // If text parsing also fails, use the original error message + } } + + logger.error('Provider API request failed', { + workflowId: context.workflowId, + blockId: block.id, + status: response.status, + statusText: response.statusText, + url: url.toString(), + errorMessage, + errorDetails, + headers: Object.fromEntries(response.headers.entries()), + }) + throw new Error(errorMessage) } @@ -579,6 +612,50 @@ export class AgentBlockHandler implements BlockHandler { } } catch (error) { logger.error('Error executing provider request:', { error }) + + // Enhanced error logging for different error types + if (error instanceof Error) { + logger.error('Provider request error details', { + workflowId: context.workflowId, + blockId: block.id, + errorName: error.name, + errorMessage: error.message, + errorStack: error.stack, + url: url.toString(), + timestamp: new Date().toISOString(), + }) + + // Check for specific error types + if (error.name === 'AbortError') { + logger.error('Request timed out after 2 minutes', { + workflowId: context.workflowId, + blockId: block.id, + url: url.toString(), + }) + throw new Error('Provider request timed out - the API took too long to respond') + } + if (error.name === 'TypeError' && error.message.includes('fetch')) { + logger.error('Network fetch error - possible connectivity issue', { + workflowId: context.workflowId, + blockId: block.id, + url: url.toString(), + errorMessage: error.message, + }) + throw new Error( + 'Network error - unable to connect to provider API. Please check your internet connection.' + ) + } + if (error.message.includes('ENOTFOUND') || error.message.includes('ECONNREFUSED')) { + logger.error('DNS/Connection error', { + workflowId: context.workflowId, + blockId: block.id, + url: url.toString(), + errorMessage: error.message, + }) + throw new Error('Unable to connect to server - DNS or connection issue') + } + } + throw error } }