diff --git a/src/__tests__/commands/search.test.ts b/src/__tests__/commands/search.test.ts index fecaeb31aa..c908189ef7 100644 --- a/src/__tests__/commands/search.test.ts +++ b/src/__tests__/commands/search.test.ts @@ -831,6 +831,20 @@ describe('executeSearch', () => { }); }); + it("shows the API's own error for a refused request, not the HTTP status", async () => { + const apiError = + 'Insufficient credits to perform this request. For more credits, you can upgrade your plan at https://firecrawl.dev/pricing or try changing the request limit to a lower value.'; + mockHttpPost.mockRejectedValue( + Object.assign(new Error('Request failed with status code 402'), { + response: { status: 402, data: { success: false, error: apiError } }, + }) + ); + + const result = await executeSearch({ query: 'test query' }); + + expect(result).toEqual({ success: false, error: apiError }); + }); + it('should handle non-Error exceptions', async () => { mockHttpPost.mockRejectedValue('String error'); diff --git a/src/commands/search.ts b/src/commands/search.ts index 39e8449a7e..52ca5e3259 100644 --- a/src/commands/search.ts +++ b/src/commands/search.ts @@ -177,13 +177,19 @@ export async function executeSearch( error: options.domainTools || options.sources?.includes('alexandria') ? JSON.stringify(apiFailure(error)) - : error instanceof Error - ? error.message - : 'Unknown error occurred', + : apiErrorMessage(error), }; } } +/** The API's own error text, which a raw HTTP error keeps only in its response body. */ +function apiErrorMessage(error: unknown): string { + const body = (error as { response?: { data?: { error?: unknown } } }) + ?.response?.data; + if (typeof body?.error === 'string' && body.error) return body.error; + return error instanceof Error ? error.message : 'Unknown error occurred'; +} + /** * Shorten a matched passage for the human-readable output. A developer passage * runs to several KB, which floods a terminal. `--json` keeps the full text.