firecrawl/firecrawl · error
Product extraction failed: service returned an unexpected re
Error message
Product extraction failed: service returned an unexpected response shape (missing 'product')
What it means
The service returned valid JSON with HTTP 2xx, but the body is not an object or is missing the `product` key. The contract is `{ product: Product | null }`; an object without `product` is a malformed response surfaced as a failure rather than silently reported as "no product found" (which is reserved for an explicit null).
Source
Thrown at apps/api/src/scraper/scrapeURL/transformers/product.ts:64
.catch(() => ({ detail: "Unknown error" }));
throw new Error(`Product extraction failed: ${error.detail}`);
}
let data: any;
try {
data = await response.json();
} catch {
throw new Error(
"Product extraction failed: service returned a non-JSON response",
);
}
// The service contract is a 200 with a body of `{ product: Product | null }`.
// A body that isn't an object, or is missing the `product` key entirely, is a
// malformed response -- surface it as a service failure rather than silently
// reporting "no product found" (which is reserved for an explicit `null`).
if (typeof data !== "object" || data === null || !("product" in data)) {
throw new Error(
"Product extraction failed: service returned an unexpected response shape (missing 'product')",
);
}
if (data.product) {
document.product = data.product;
} else {
// `product` is null: the page loaded but is not a product page.
document.warning =
"No product found on this page; it does not appear to be a product page." +
(document.warning ? " " + document.warning : "");
}
return document;
}
View on GitHub (pinned to 656bffcc28)
Solutions
- Inspect the actual JSON body the service returns for this input.
- Align the service to return { product: Product | null }.
- Pin or coordinate the service version with the API deployment.
- Add a contract test covering the response shape.
Defensive patterns
Strategy: try-catch
Type guard
// If you control the fetch, narrow the body before using it:
function isProductEnvelope(data: unknown): data is { product: unknown } {
return typeof data === 'object' && data !== null && 'product' in data;
} Try / catch
try {
await fetchProduct(meta, document);
} catch (e) {
if (e.message.includes('unexpected response shape')) {
meta.logger.error('Product service schema drift', { error: e.message });
}
throw e;
} Prevention
- Add an integration test asserting the { product: Product | null } envelope.
- Version the service API and coordinate schema changes with the API team.
When it happens
Trigger: typeof data !== 'object', data === null, or !('product' in data) at product.ts:63. The service returned e.g. {} or { error: '...' } or an array.
Common situations: Version skew between the API and the extraction service (new response schema); service returns an error object with a 200 status; service returns a top-level array instead of an object.
Related errors
- Product extraction failed: service returned a non-JSON respo
- Product extraction failed: ${error.detail}
- Video service returned invalid supported URL patterns
- Video download failed: avgrab service returned an invalid re
- Array key not found in schema
AI-assisted analysis of firecrawl/firecrawl@656bffcc28 (2026-08-12).
Data as JSON: /api/errors/0354ec41372cdbd7.
Report an issue: GitHub.