firecrawl/open-lovable · error · Error
Firecrawl API error: ${error}
Error message
Firecrawl API error: ${error} What it means
This error is thrown by the POST handler in app/api/scrape-url-enhanced/route.ts:66 when the raw fetch call to https://api.firecrawl.dev/v1/scrape returns a non-2xx HTTP status. The developer reads the response body as text and prefixes it with 'Firecrawl API error:' so the upstream API's error payload (e.g. 401 unauthorized, 402 payment required, 429 rate limit, 5xx) is surfaced verbatim. It is a generic wrapper for any Firecrawl HTTP-level failure, as opposed to the 2xx-but-unsuccessful case handled on the next lines.
Source
Thrown at app/api/scrape-url-enhanced/route.ts:66
timeout: 30000,
blockAds: true,
maxAge: 3600000, // Use cached data if less than 1 hour old (500% faster!)
actions: [
{
type: 'wait',
milliseconds: 2000
},
{
type: 'screenshot',
fullPage: false // Just visible viewport for performance
}
]
})
});
if (!firecrawlResponse.ok) {
const error = await firecrawlResponse.text();
throw new Error(`Firecrawl API error: ${error}`);
}
const data = await firecrawlResponse.json();
if (!data.success || !data.data) {
throw new Error('Failed to scrape content');
}
const { markdown, metadata, screenshot, actions } = data.data;
// html available but not used in current implementation
// Get screenshot from either direct field or actions result
const screenshotUrl = screenshot || actions?.screenshots?.[0] || null;
// Sanitize the markdown content
const sanitizedMarkdown = sanitizeQuotes(markdown || '');
// Extract structured data from the responseView on GitHub (pinned to 69bd93bae7)
Solutions
- Log firecrawlResponse.status alongside the body text so the actual upstream cause (401 vs 402 vs 429 vs 5xx) is visible, then fix that specific cause first.
- Verify FIRECRAWL_API_KEY is set in the deployment environment and is valid — test with curl -H 'Authorization: Bearer $KEY' https://api.firecrawl.dev/v1/scrape.
- Check your Firecrawl dashboard for credit/plan exhaustion or rate limits; upgrade the plan or add backoff/retry on 429.
- Retry transient failures (429/5xx) with exponential backoff; for slow sites consider raising the timeout/waitFor options or dropping the extra screenshot actions.
- Return a structured error to the client (status code plus sanitized message) instead of leaking the raw upstream response body.
Example fix
// before
if (!firecrawlResponse.ok) {
const error = await firecrawlResponse.text();
throw new Error(`Firecrawl API error: ${error}`);
}
// after
if (!firecrawlResponse.ok) {
const error = await firecrawlResponse.text();
if (firecrawlResponse.status === 429 || firecrawlResponse.status >= 500) {
// retry with backoff, e.g. via p-retry
}
console.error(`Firecrawl scrape failed (${firecrawlResponse.status}):`, error);
return NextResponse.json({ success: false, error: `Scrape failed (${firecrawlResponse.status})` }, { status: firecrawlResponse.status === 401 ? 500 : 502 });
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!process.env.FIRECRAWL_API_KEY) {
throw new Error('FIRECRAWL_API_KEY is not configured');
}
try { new URL(url); } catch { throw new Error(`Invalid URL: ${url}`); } Type guard
function isFirecrawlOk(res: Response) { return res.ok; } Try / catch
try {
const res = await fetch('https://api.firecrawl.dev/v1/scrape', { ... });
if (!res.ok) {
const body = await res.text();
if (res.status === 429 || res.status >= 500) throw new RetryableError(`Firecrawl ${res.status}: ${body}`);
throw new Error(`Firecrawl ${res.status}: ${body.slice(0, 300)}`);
}
} catch (e) {
if (e instanceof RetryableError) { /* backoff + retry */ }
return NextResponse.json({ success: false, error: 'Scrape unavailable' }, { status: 502 });
} Prevention
- Always check both res.ok and res.status before parsing the body, and log the status with the body text.
- Validate the URL is public and well-formed before calling Firecrawl (reject localhost/private IPs).
- Monitor Firecrawl credit usage and set alerts before quota exhaustion.
- Implement exponential backoff retry for 429/5xx responses only.
- Never surface the raw upstream body to end users; map it to a friendly message.
When it happens
Trigger: Any POST to /api/scrape-url-enhanced where api.firecrawl.dev/v1/scrape responds with a non-ok status: invalid or missing FIRECRAWL_API_KEY (401), exhausted credits/plan limit (402/429), malformed request body (400), a URL Firecrawl refuses to scrape (blocked/paywalled site), or Firecrawl-side outage/timeout exceeding the 30000ms timeout option.
Common situations: Deployed app where FIRECRAWL_API_KEY env var was never set or rotated/revoked; free-tier credit exhaustion after heavy scraping; scraping sites that block bots (403) or require JS/captcha; passing internal/localhost URLs which Firecrawl rejects; Firecrawl incidents or rate limiting under concurrent load.
Related errors
- Search failed
- Firecrawl API returned ${firecrawlResponse.status}
- Failed to scrape content
- Failed to scrape website
- Failed to install packages: ${response.statusText}
AI-assisted analysis of firecrawl/open-lovable@69bd93bae7 (2026-08-28).
Data as JSON: /api/errors/d5f3421936120da4.
Report an issue: GitHub.