koala73/worldmonitor · error
Exa returned no content for ${url}
Error message
Exa returned no content for ${url} What it means
ExaProvider.fetch calls exa-js getContents([url]) and indexes results[0]. Exa answered HTTP 200 but returned an empty results array — it has no cached or crawlable content for that exact URL — so the provider throws rather than fabricate an empty page (html/markdown stay coupled in FetchResult).
Source
Thrown at consumer-prices-core/src/acquisition/exa.ts:23
readonly name = 'exa' as const;
private readonly apiKey: string;
private readonly baseUrl = 'https://api.exa.ai';
private client: Exa;
constructor(apiKey: string) {
this.apiKey = apiKey;
this.client = new Exa(apiKey);
}
async fetch(url: string, _opts: FetchOptions = {}): Promise<FetchResult> {
const result = await this.client.getContents([url], {
text: { maxCharacters: 100_000 },
highlights: { numSentences: 5, highlightsPerUrl: 3 },
});
const item = result.results[0];
if (!item) throw new Error(`Exa returned no content for ${url}`);
return {
url,
html: item.text ?? '',
markdown: item.text ?? '',
statusCode: 200,
provider: this.name,
fetchedAt: new Date(),
metadata: { highlights: item.highlights },
};
}
async search(query: string, opts: SearchOptions = {}): Promise<SearchResult[]> {
const result = await this.request<{
results?: Array<{
url: string;
title?: string;
text?: string;View on GitHub (pinned to eeab0a219f)
Solutions
- Verify the URL loads in a browser to rule out a typo or 404
- Fall back to another acquisition provider for that URL (e.g. Firecrawl scrape) instead of retrying Exa
- Normalize to the canonical URL (strip tracking params) or submit the URL to Exa for crawling
- Treat it as a per-URL condition: skip and continue the batch, not a provider outage
Example fix
// before
const result = await exa.fetch(url); // throws 'Exa returned no content for ...'
// after — degrade to the scrape provider for this URL only
let result;
try {
result = await exa.fetch(url);
} catch (err) {
if (!/returned no content/.test(err.message)) throw err;
result = await firecrawl.fetch(url);
} Defensive patterns
Strategy: fallback
Validate before calling
// Cheap liveness check before spending an Exa call
async function urlLooksLive(url) {
try { const res = await fetch(url, { method: 'HEAD' }); return res.status < 400; }
catch { return false; }
} Type guard
interface ExaContentsResult { results?: Array<{ text?: string; highlights?: string[] }> }
function hasContentRow(r: ExaContentsResult | undefined): r is { results: NonNullable<ExaContentsResult['results']> } {
return Array.isArray(r?.results) && r.results.length > 0;
} Try / catch
try {
return await exa.fetch(url);
} catch (err) {
if (err instanceof Error && /returned no content/.test(err.message)) {
return await firecrawl.fetch(url); // per-URL fallback, provider stays healthy
}
throw err; // auth/quota/HTTP errors are not content gaps
} Prevention
- Chain providers (Exa → Firecrawl) so empty-content pages degrade instead of failing the run
- Canonicalize URLs (strip tracking params) to maximize Exa index hits
- Track per-URL no-content rates separately from provider error rates
When it happens
Trigger: URL unknown to Exa's index (freshly published page, obscure site, typo'd URL); paywalled or robots-excluded content Exa will not serve; a URL whose canonical form differs from the requested one, so Exa stores it under another key.
Common situations: Scraping newly published product pages; deep product URLs carrying session or tracking parameters; niche retailer domains with thin Exa coverage.
Related errors
- Firecrawl error: ${data.error ?? 'unknown'}
- Exa returned malformed structured summary for ${url}
- Exa ${endpoint.slice(1)} failed HTTP ${response.status}: ${d
- Firecrawl scrape failed: HTTP ${resp.status}
- Firecrawl extract error: ${data.error ?? 'unknown'}
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/3f7837c5cbb88869.
Report an issue: GitHub.