jackwener/OpenCLI · error · Error

extraction_drift

extraction_drift

Error message

[taxonomy=extraction_drift] site=${site} command=search all rows rejected by quality gate (raw=${raw count})

What it means

toProcurementSearchRecords applies a per-site quality gate (qualityRejectReason) to each raw search row. If raw rows exist but every one is rejected, the library assumes the site's markup changed and parsers are extracting wrong data, so it throws a taxonomy error with code 'extraction_drift' rather than returning an empty-but-misleading result set.

Source

Thrown at clis/jianyu/shared/procurement-contract.js:291

    return deduped;
}
export function formatTaxonomyError(taxonomy, { site, command, detail, }) {
    return `[taxonomy=${taxonomy}] site=${site} command=${command} ${cleanText(detail)}`;
}
export function taxonomyError(taxonomy, context) {
    return new Error(formatTaxonomyError(taxonomy, context));
}
export function toProcurementSearchRecords(rows, { site, query, limit, }) {
    const normalizedRows = dedupeByTitleUrl(rows.map((row) => normalizeCoreRecord(row, { sourceSite: site })));
    const accepted = [];
    for (const row of normalizedRows) {
        const rejectReason = qualityRejectReason(row, query);
        if (rejectReason)
            continue;
        accepted.push(row);
    }
    if (normalizedRows.length > 0 && accepted.length === 0) {
        throw taxonomyError('extraction_drift', {
            site,
            command: 'search',
            detail: `all rows rejected by quality gate (raw=${normalizedRows.length})`,
        });
    }
    return accepted
        .slice(0, Math.max(1, limit))
        .map((row, index) => ({
        rank: index + 1,
        ...row,
    }));
}
export function toProcurementDetailRecord({ title, url, contextText, publishTime, }, { site, query = '', }) {
    const core = normalizeCoreRecord({
        title,
        url,
        date: publishTime,
        contextText,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Dump the raw rows and check why each fails qualityRejectReason, then fix the parser/normalizer
  2. Update selectors/regexes for the drifted site layout
  3. Relax the quality gate only if the rows are genuinely valid
  4. Pin/upgrade the library version that supports the current site markup
Defensive patterns

Strategy: validation

Validate before calling

function rowsLookSane(rows) {
  return rows.length === 0 || rows.some(r => r.title && r.title.length > 3 && r.url);
}
if (!rowsLookSane(rawRows)) console.warn('quality gate may reject all rows for site', site);

Type guard

function isExtractionDrift(e) {
  return e instanceof Error && /\[taxonomy=extraction_drift\]/.test(e.message);
}

Try / catch

try {
  rows = await procurementSearch(page, { site, query });
} catch (e) {
  if (isExtractionDrift(e)) {
    // all rows rejected: flag site for parser review, do not treat as empty results
    reportDrift(site, 'search');
    return [];
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the jianyu procurement 'search' command for a site where the parser returns rows but none pass qualityRejectReason (e.g. missing/malformed titles, wrong fields, filtered-out noise rows).

Common situations: Site redesign or markup changes breaking field extraction; quality-gate thresholds tightened while the site's data format shifted; scraping a site variant whose layout differs from the supported template; proxy/mirror serving stale or rewritten HTML.

Related errors


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