{"record":{"id":"2949ade528015e51","repo":"jackwener/OpenCLI","slug":"guazi-car-clueid","errorCode":null,"errorMessage":"guazi car ${clueId}","messagePattern":"guazi car (.+?)","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/guazi/car.js","lineNumber":100,"sourceCode":"\ncli({\n    site: 'guazi',\n    name: 'car',\n    access: 'read',\n    aliases: ['detail'],\n    description: '瓜子二手车车源详情（售价 / 上牌 / 里程 / 过户 / 配置 / 车况）',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'clue_id', required: true, positional: true, help: '车源 ID（来自 browse 的 clue_id，或 /car-detail/c<id>.html URL）' },\n    ],\n    columns: CAR_COLUMNS,\n    func: async (args) => {\n        const clueId = normalizeClueId(args.clue_id);\n        const html = await guaziFetch(`/car-detail/c${clueId}.html`, `car ${clueId}`);\n        const rows = parseCarDetail(html, clueId);\n        if (!hasData(rows)) {\n            throw new EmptyResultError(\n                `guazi car ${clueId}`,\n                'No listing detail found — the car may have been sold/removed, or the id is wrong.',\n            );\n        }\n        const map = Object.fromEntries(rows.map((r) => [r.field, r.value]));\n        requireText(map.title, `guazi car ${clueId} title`);\n        requireText(map.price, `guazi car ${clueId} price`);\n        return rows;\n    },\n});\n","sourceCodeStart":82,"sourceCodeEnd":111,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/guazi/car.js#L82-L111","documentation":"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>'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the car still exists by opening https://m.guazi.com/car-detail/c<id>.html in a browser.","Re-run 'guazi browse' to get a fresh list of current clue ids and pick a live one.","Double-check the id — use normalizeClueId-accepted forms (bare number or /car-detail/c<id>.html URL).","If the page shows content in a browser but parsing fails, update parseCarDetail selectors to the new markup."],"exampleFix":"// before\nguazi car --clue_id 100200300 // stale id\n// after\nconst rows = await guaziBrowse({ city: 'bj' });\nguazi car --clue_id rows[0].clue_id // fresh id from browse","handlingStrategy":"try-catch","validationCode":"const res = await fetch(`https://m.guazi.com/car-detail/c${clueId}.html`, { redirect: 'manual' });\nif (res.status >= 300) console.warn(`car ${clueId} may be removed (status ${res.status})`);","typeGuard":"function hasDetailRows(rows) { return Array.isArray(rows) && rows.length > 0; }","tryCatchPattern":"try {\n  const car = await guaziCar({ clue_id: id });\n} catch (e) {\n  if (/No listing detail found/.test(e.message)) {\n    console.warn(`Car ${id} unavailable; refreshing from browse`);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Use fresh clue ids from a recent browse call, not cached ones","Verify the URL in a browser when a lookup fails","Treat single-car failures as non-fatal in batch jobs","Log the id for later re-check rather than retrying immediately"],"tags":["scraping","empty-results","stale-data"],"backgroundTag":"scraper-empty-result","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}