jackwener/OpenCLI · error · EmptyResultError

powerchina search

Error message

powerchina search

What it means

An EmptyResultError thrown by the powerchina search command when the DOM extraction produced rows, but every extracted item was classified as a navigation/portal row (site chrome like 'Home', 'English', search links) rather than a real bid entry. The library throws this to signal 'we reached the page and parsed something, but zero actual bid records matched', distinguishing it from a true empty result set or an auth wall.

Source

Thrown at clis/powerchina/search.js:218

        const message = cleanText(error instanceof Error ? error.message : String(error || ''));
        if (RETRYABLE_SEARCH_ERROR_HINT.test(message)) {
          throw new Error(`[taxonomy=relay_unavailable] site=powerchina command=search detached browser context: ${message}`);
        }
        throw error;
      }
    }

    const rows = filterNavigationRows(
      dedupeCandidates(extractedRows).map((item) => ({
        title: cleanText(item.title),
        url: cleanText(item.url),
        date: normalizeDate(cleanText(item.date)),
        contextText: cleanText(item.contextText),
      })),
    );

    if (rows.length === 0 && extractedRows.length > 0) {
      throw new EmptyResultError('powerchina search', 'extracted only navigation/portal rows, no bid entries matched');
    }

    if (rows.length === 0) {
      const pageText = cleanText(await page.evaluate('document.body ? document.body.innerText : ""'));
      if (/(请先登录|未登录|登录后|验证码|人机验证)/.test(pageText)) {
        throw new AuthRequiredError(
          'bid.powerchina.cn',
          '[taxonomy=selector_drift] site=powerchina command=search login required or human verification',
        );
      }
      if (apiFailure) {
        throw new EmptyResultError('powerchina search', `api/dom yielded no result: ${apiFailure}`);
      }
    }

    return toProcurementSearchRecords(rows, {
      site: 'powerchina',
      query,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry with a more specific Chinese keyword so the search page returns real bid entries instead of only portal navigation.
  2. Check whether bid.powerchina.cn changed its search-results markup and update the extractor/filter hints.
  3. Inspect the raw page (or apiFailure info) to confirm whether the query genuinely has no results.
  4. Fall back to the API endpoint (BidAnnouncementSummary/list) path if DOM extraction keeps returning only navigation rows.

Example fix

// before
await cli search powerchina "home"
// after
await cli search powerchina "输变电项目 招标公告"
Defensive patterns

Strategy: validation

Validate before calling

// validate query contains bid-relevant keywords before calling
const BID_HINT = /(公告|招标|采购|中标|成交|项目|notice|tender|bidding)/i;
if (!BID_HINT.test(query)) {
  throw new Error(`query "${query}" unlikely to match bid entries; add tender/procurement keywords`);
}

Type guard

function hasBidResults(result) {
  return Array.isArray(result?.rows) && result.rows.length > 0;
}

Try / catch

try {
  rows = await searchPowerchina(query);
} catch (err) {
  if (err instanceof EmptyResultError && /navigation\/portal rows/.test(err.message)) {
    console.warn('Only portal chrome extracted — refine query or check site markup');
    rows = [];
  } else throw err;
}

Prevention

When it happens

Trigger: Running powerchina search where extractedRows.length > 0 but filterNavigationRows removes all of them because none match PROCUREMENT_TITLE_HINT (公告/招标/采购/中标/成交/项目/notice/tender/bidding) or all match NAVIGATION_TITLE_HINT / navigation URL patterns.

Common situations: Site layout change on bid.powerchina.cn moving bid entries into elements the extractor no longer sees; search returning only the portal shell (no results page content); query yielding zero real hits so only header/footer links were scraped; locale/English version of the site rendering titles that don't match the title hint regex.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/65419b52d2c6b8e3. Report an issue: GitHub.