diff --git a/apps/sim/executor/handlers/evaluator/evaluator-handler.test.ts b/apps/sim/executor/handlers/evaluator/evaluator-handler.test.ts index e70015a68bc..0175ee41fa7 100644 --- a/apps/sim/executor/handlers/evaluator/evaluator-handler.test.ts +++ b/apps/sim/executor/handlers/evaluator/evaluator-handler.test.ts @@ -287,6 +287,29 @@ describe('EvaluatorBlockHandler', () => { expect((result as any).fluency).toBe(0) }) + it('should throw a clear error when provider returns no content', async () => { + const inputs = { + content: 'Test content to evaluate', + metrics: [{ name: 'quality', description: 'Quality', range: { min: 0, max: 10 } }], + apiKey: 'test-api-key', + } + + mockFetch.mockImplementationOnce(() => { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve({ + model: 'gpt-4o', + tokens: { input: 50, output: 0, total: 50 }, + }), + }) + }) + + await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow( + 'Provider returned an empty response' + ) + }) + it('should extract metric scores ignoring case', async () => { const inputs = { content: 'Test', diff --git a/apps/sim/executor/handlers/evaluator/evaluator-handler.ts b/apps/sim/executor/handlers/evaluator/evaluator-handler.ts index 95d25ba4656..f1370a983fa 100644 --- a/apps/sim/executor/handlers/evaluator/evaluator-handler.ts +++ b/apps/sim/executor/handlers/evaluator/evaluator-handler.ts @@ -145,6 +145,12 @@ export class EvaluatorBlockHandler implements BlockHandler { const result = await response.json() + if (!result.content) { + throw new Error( + 'Provider returned an empty response. The model may be unavailable or the request was malformed.' + ) + } + const parsedContent = this.extractJSONFromResponse(result.content) const metricScores = this.extractMetricScores(parsedContent, inputs.metrics) diff --git a/apps/sim/executor/handlers/router/router-handler.test.ts b/apps/sim/executor/handlers/router/router-handler.test.ts index 5d4defa0a0b..00ec7ff495e 100644 --- a/apps/sim/executor/handlers/router/router-handler.test.ts +++ b/apps/sim/executor/handlers/router/router-handler.test.ts @@ -244,6 +244,45 @@ describe('RouterBlockHandler', () => { await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow('Server error') }) + it('should throw a clear error when provider returns no content', async () => { + const inputs = { prompt: 'Choose the best option.', apiKey: 'test-api-key' } + + mockFetch.mockImplementationOnce(() => { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve({ + model: 'gpt-4o', + tokens: { input: 100, output: 0, total: 100 }, + }), + }) + }) + + await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow( + 'Provider returned an empty response' + ) + }) + + it('should throw a clear error when provider content is empty string', async () => { + const inputs = { prompt: 'Choose the best option.', apiKey: 'test-api-key' } + + mockFetch.mockImplementationOnce(() => { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve({ + content: '', + model: 'gpt-4o', + tokens: { input: 100, output: 0, total: 100 }, + }), + }) + }) + + await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow( + 'Provider returned an empty response' + ) + }) + it('should handle Azure OpenAI models with endpoint and API version', async () => { const inputs = { prompt: 'Choose the best option.', @@ -587,4 +626,30 @@ describe('RouterBlockHandler V2', () => { expect(result.selectedRoute).toBe('route-1') expect(result.reasoning).toBe('') }) + + it('should throw a clear error when provider returns no content (V2)', async () => { + const inputs = { + context: 'I need help with billing', + model: 'gpt-4o', + apiKey: 'test-api-key', + routes: JSON.stringify([ + { id: 'route-support', title: 'Support', value: 'Customer support inquiries' }, + ]), + } + + mockFetch.mockImplementationOnce(() => { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve({ + model: 'gpt-4o', + tokens: { input: 150, output: 0, total: 150 }, + }), + }) + }) + + await expect(handler.execute(mockContext, mockRouterV2Block, inputs)).rejects.toThrow( + 'Provider returned an empty response' + ) + }) }) diff --git a/apps/sim/executor/handlers/router/router-handler.ts b/apps/sim/executor/handlers/router/router-handler.ts index f4c715d2ced..fad573aa637 100644 --- a/apps/sim/executor/handlers/router/router-handler.ts +++ b/apps/sim/executor/handlers/router/router-handler.ts @@ -124,6 +124,12 @@ export class RouterBlockHandler implements BlockHandler { const result = await response.json() + if (!result.content) { + throw new Error( + 'Provider returned an empty response. The model may be unavailable or the request was malformed.' + ) + } + const chosenBlockId = result.content.trim().toLowerCase() const chosenBlock = targetBlocks?.find((b) => b.id === chosenBlockId) @@ -273,6 +279,12 @@ export class RouterBlockHandler implements BlockHandler { const result = await response.json() + if (!result.content) { + throw new Error( + 'Provider returned an empty response. The model may be unavailable or the request was malformed.' + ) + } + let chosenRouteId: string let reasoning = ''