firecrawl/open-lovable · error · Error
Search failed
Error message
Search failed
What it means
Thrown at app/api/search/route.ts:29 when the raw fetch to https://api.firecrawl.dev/v1/search returns a non-ok HTTP status. Unlike the scrape routes, this handler discards the response body and status, so the real upstream cause (401 bad key, 429 quota, 400 bad query, 5xx outage) is lost and every failure surfaces as the opaque string 'Search failed'. The catch block then converts it into a 500 'Failed to perform search' for the client.
Source
Thrown at app/api/search/route.ts:29
// Use Firecrawl search to get top 10 results with screenshots
const searchResponse = await fetch('https://api.firecrawl.dev/v1/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.FIRECRAWL_API_KEY}`,
},
body: JSON.stringify({
query,
limit: 10,
scrapeOptions: {
formats: ['markdown', 'screenshot'],
onlyMainContent: true,
},
}),
});
if (!searchResponse.ok) {
throw new Error('Search failed');
}
const searchData = await searchResponse.json();
// Format results with screenshots and markdown
const results = searchData.data?.map((result: any) => ({
url: result.url,
title: result.title || result.url,
description: result.description || '',
screenshot: result.screenshot || null,
markdown: result.markdown || '',
})) || [];
return NextResponse.json({ results });
} catch (error) {
console.error('Search error:', error);
return NextResponse.json(
{ error: 'Failed to perform search' },View on GitHub (pinned to 69bd93bae7)
Solutions
- Capture searchResponse.status and body text in the thrown error/log so the true cause is diagnosable — this is the single most important fix.
- Verify FIRECRAWL_API_KEY is set in the environment (this route, unlike scrape-website, never checks for it and silently sends an empty credential).
- Confirm the /v1/search request payload (limit:10, scrapeOptions formats) against current Firecrawl docs; invalid scrapeOptions can yield 400.
- Add retry with backoff for 429/5xx responses and return a mapped status (401->misconfigured, 429->rate limited) instead of a blanket 500.
- Set up a Firecrawl status/health check or alerting so outages are recognized quickly.
Example fix
// before
if (!searchResponse.ok) {
throw new Error('Search failed');
}
// after
if (!searchResponse.ok) {
const detail = await searchResponse.text();
console.error(`Firecrawl search failed (${searchResponse.status}):`, detail);
throw new Error(`Search failed (${searchResponse.status}): ${detail.slice(0, 200)}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
const FIRECRAWL_API_KEY = process.env.FIRECRAWL_API_KEY;
if (!FIRECRAWL_API_KEY) throw new Error('FIRECRAWL_API_KEY is not set');
if (typeof query !== 'string' || query.trim().length === 0) throw new Error('Query is required'); Type guard
function isFirecrawlSearchOk(res: Response) { return res.ok; } Try / catch
const res = await fetch('https://api.firecrawl.dev/v1/search', { ... });
if (!res.ok) {
const detail = await res.text().catch(() => '');
throw new Error(`Firecrawl search ${res.status}: ${detail.slice(0, 200)}`);
}
// in outer catch:
catch (e) {
console.error('Search error:', e);
const status = (e as Error).message.includes('401') ? 503 : 502;
return NextResponse.json({ error: 'Search unavailable, try again later' }, { status });
} Prevention
- Never throw opaque messages on non-ok responses — include status code and response body in logs.
- Guard for a missing FIRECRAWL_API_KEY before making the call (this route currently omits that check).
- Validate the search payload against current Firecrawl /v1/search docs (scrapeOptions support varies by version).
- Add backoff/retry on 429 and 5xx; surface a distinct status for auth/quota problems.
- Monitor Firecrawl API status and quota usage proactively.
When it happens
Trigger: Any POST /api/search where the Firecrawl /v1/search endpoint responds non-2xx: missing/invalid FIRECRAWL_API_KEY, exhausted credits or rate limit, a query payload Firecrawl rejects (e.g. unsupported scrapeOptions combination like markdown+screenshot), or a Firecrawl service outage.
Common situations: Environment without FIRECRAWL_API_KEY configured (the header becomes 'Bearer undefined' and Firecrawl returns 401); free-tier quota exhausted after many searches; sending scrapeOptions fields not supported by the deployed Firecrawl version; network/egress restrictions in the hosting environment blocking api.firecrawl.dev.
Related errors
- Firecrawl API error: ${error}
- Firecrawl API returned ${firecrawlResponse.status}
- Firecrawl API key not configured
- No branding data in Firecrawl response
- Failed to capture screenshot
AI-assisted analysis of firecrawl/open-lovable@69bd93bae7 (2026-08-28).
Data as JSON: /api/errors/8711b3529847f457.
Report an issue: GitHub.