jackwener/OpenCLI · error · EmptyResultError

guazi car ${clueId}

Error message

guazi car ${clueId}

What it means

The 'guazi car' command fetched the detail page /car-detail/c<clueId>.html but parseCarDetail yielded no data rows (hasData false), so EmptyResultError is thrown. This tells you the listing detail could not be extracted — most often because the car is gone or the id is wrong — and includes the command label 'guazi car <clueId>'.

Source

Thrown at clis/guazi/car.js:100

cli({
    site: 'guazi',
    name: 'car',
    access: 'read',
    aliases: ['detail'],
    description: '瓜子二手车车源详情(售价 / 上牌 / 里程 / 过户 / 配置 / 车况)',
    strategy: Strategy.PUBLIC,
    browser: false,
    args: [
        { name: 'clue_id', required: true, positional: true, help: '车源 ID(来自 browse 的 clue_id,或 /car-detail/c<id>.html URL)' },
    ],
    columns: CAR_COLUMNS,
    func: async (args) => {
        const clueId = normalizeClueId(args.clue_id);
        const html = await guaziFetch(`/car-detail/c${clueId}.html`, `car ${clueId}`);
        const rows = parseCarDetail(html, clueId);
        if (!hasData(rows)) {
            throw new EmptyResultError(
                `guazi car ${clueId}`,
                'No listing detail found — the car may have been sold/removed, or the id is wrong.',
            );
        }
        const map = Object.fromEntries(rows.map((r) => [r.field, r.value]));
        requireText(map.title, `guazi car ${clueId} title`);
        requireText(map.price, `guazi car ${clueId} price`);
        return rows;
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the car still exists by opening https://m.guazi.com/car-detail/c<id>.html in a browser.
  2. Re-run 'guazi browse' to get a fresh list of current clue ids and pick a live one.
  3. Double-check the id — use normalizeClueId-accepted forms (bare number or /car-detail/c<id>.html URL).
  4. If the page shows content in a browser but parsing fails, update parseCarDetail selectors to the new markup.

Example fix

// before
guazi car --clue_id 100200300 // stale id
// after
const rows = await guaziBrowse({ city: 'bj' });
guazi car --clue_id rows[0].clue_id // fresh id from browse
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await fetch(`https://m.guazi.com/car-detail/c${clueId}.html`, { redirect: 'manual' });
if (res.status >= 300) console.warn(`car ${clueId} may be removed (status ${res.status})`);

Type guard

function hasDetailRows(rows) { return Array.isArray(rows) && rows.length > 0; }

Try / catch

try {
  const car = await guaziCar({ clue_id: id });
} catch (e) {
  if (/No listing detail found/.test(e.message)) {
    console.warn(`Car ${id} unavailable; refreshing from browse`);
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling 'guazi car --clue_id <id>' where the page loads (HTTP success) but contains no parseable detail rows: the car was sold/removed, the clue id does not exist, or the detail template changed.

Common situations: Re-using a clue id captured days earlier from a browse listing that has since sold; a typo in the id; Guazi redirecting removed cars to a generic page that still returns 200 with an empty shell.

Related errors


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