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

  1. Inspect the actual JSON body the service returns for this input.
  2. Align the service to return { product: Product | null }.
  3. Pin or coordinate the service version with the API deployment.
  4. 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

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


AI-assisted analysis of firecrawl/firecrawl@656bffcc28 (2026-08-12). Data as JSON: /api/errors/0354ec41372cdbd7. Report an issue: GitHub.