koala73/worldmonitor · error
ExaSearchAdapter does not support single-product parsing
Error message
ExaSearchAdapter does not support single-product parsing
What it means
ExaSearchAdapter is a search-mode adapter: its fetchTarget() already produces parsed products straight from Exa results (with optional Firecrawl fallback extraction), so parseProduct() has nothing to do and throws unconditionally. Seeing this error means the pipeline treated the adapter like a page-scraping adapter (fetch then parse) — a protocol mismatch, not a data problem.
Source
Thrown at consumer-prices-core/src/adapters/exa-search.ts:317
inStock: true,
rawPayload: {
exaUrl: payload.firecrawlUrl,
firecrawlFallback: true,
basketSlug: payload.basketSlug,
itemCategory: payload.itemCategory,
canonicalName: payload.canonicalName,
},
},
];
}
ctx.logger.warn(` [firecrawl-fallback] ${payload.canonicalName}: no ${currency} price found in Firecrawl markdown either`);
}
return [];
}
async parseProduct(_ctx: AdapterContext, _result: FetchResult): Promise<ParsedProduct> {
throw new Error('ExaSearchAdapter does not support single-product parsing');
}
async validateConfig(config: RetailerConfig): Promise<string[]> {
const errors: string[] = [];
if (!this.apiKey) errors.push('EXA_API_KEY env var is required for adapter: exa-search');
if (!config.baseUrl) errors.push('baseUrl is required');
return errors;
}
}
View on GitHub (pinned to eeab0a219f)
Solutions
- Remove the parse step for retailers using exa-search — products arrive already parsed from fetchTarget
- Dispatch on adapter capability: search-mode adapters skip parseProduct entirely
- Add an explicit adapter kind ('search' vs 'scrape') to the contract so runners branch on it instead of discovering this by exception
Example fix
// before — assumes every adapter is fetch-then-parse const result = await adapter.fetchTarget(ctx, target); const product = await adapter.parseProduct(ctx, result); // after — branch on adapter kind const result = await adapter.fetchTarget(ctx, target); const product = adapter.key === 'exa-search' ? productsFromSearchResult(result) // already parsed inside the search payload : await adapter.parseProduct(ctx, result);
Defensive patterns
Strategy: type-guard
Validate before calling
// when assembling pipeline stages
function requiresParseStage(adapter: RetailerAdapter): boolean {
return adapter.key !== 'exa-search'; // search-mode adapters finish parsing in fetchTarget
}
if (requiresParseStage(adapter)) stages.push(parseStage); Type guard
function isSearchModeAdapter(a: RetailerAdapter): boolean {
return a.key === 'exa-search'; // parseProduct() throws by design on this adapter
} Try / catch
try {
product = await adapter.parseProduct(ctx, result);
} catch (err) {
if (err instanceof Error && err.message.includes('does not support single-product parsing')) {
product = productsFromSearchResult(result); // search adapters embed parsed products in the payload
} else {
throw err;
}
} Prevention
- Make adapter capability explicit in the contract (kind: 'search' | 'scrape') instead of relying on throws
- Validate pipeline stage lists per retailer adapter at startup
- In tests, exercise each adapter through its own supported protocol, not a universal fetch+parse loop
When it happens
Trigger: A generic runner calling adapter.parseProduct(ctx, result) after fetchTarget() because the retailer config selected a scrape-style pipeline; harness code that round-trips every adapter through fetch+parse; new code assuming RetailerAdapter always implements parseProduct.
Common situations: Adding exa-search to a retailer whose pipeline stage list still includes a parse step; test suites iterating all adapters with a universal fetch-then-parse contract; a refactor moving parsing into a shared step without checking adapter capability.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- EXA_API_KEY is required for exa-search adapter
- Generic adapter requires acquisition config (retailer: ${ctx
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/d3faaccdf7deeef6.
Report an issue: GitHub.