jackwener/OpenCLI · error · CommandExecutionError

guazi browse ${code}

Error message

guazi browse ${code}

What it means

The 'guazi browse' command fetched the Guazi mobile SSR listing page successfully but parseListings found zero listing anchor elements. The CLI deliberately throws CommandExecutionError instead of returning an empty result, because an empty page usually means the site's HTML layout changed (or the city page has no cars), not that there is genuinely nothing to show.

Source

Thrown at clis/guazi/browse.js:88

    site: 'guazi',
    name: 'browse',
    access: 'read',
    aliases: ['list'],
    description: '瓜子二手车在售车源列表(按城市,含售价/首付/里程/年份)',
    strategy: Strategy.PUBLIC,
    browser: false,
    args: [
        { name: 'city', positional: true, help: '城市名(北京/上海/...)或瓜子城市码(bj/sh/...)。默认 bj 北京' },
        { name: 'limit', type: 'int', default: 20, help: '返回的车源数量(最多 40,单页 SSR 上限)' },
    ],
    columns: BROWSE_COLUMNS,
    func: async (args) => {
        const code = resolveCityCode(args.city);
        const limit = requireLimit(args.limit, 20, 40);
        const html = await guaziFetch(`/${code}/buy/`, `browse ${code}`);
        const rows = parseListings(html, limit);
        if (rows.length === 0) {
            throw new CommandExecutionError(
                `guazi browse ${code}`,
                'No SSR listing anchors found on a successful Guazi mobile page; the mobile layout may have changed.',
            );
        }
        return rows;
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run with a different city (e.g. bj) to see if it is city-specific or site-wide.
  2. Fetch https://m.guazi.com/<code>/buy/ in a browser and confirm whether listing anchors still appear in the server-rendered HTML.
  3. Update the parseListings selectors in clis/guazi to match the new markup.
  4. If the site is now client-rendered, switch to a headless browser or the underlying data API.

Example fix

// before (assuming old selector)
const anchors = doc.querySelectorAll('a[href*="/car"]');
// after (match the new markup discovered by inspecting the page)
const anchors = doc.querySelectorAll('a.carlist-item, a[href*="/buy/detail"]');
Defensive patterns

Strategy: fallback

Validate before calling

const html = await fetch(`https://m.guazi.com/${code}/buy/`).then(r => r.text());
if (!/<a[^>]+href=["']?[^"']*car/i.test(html)) {
  console.warn('No SSR anchors present — layout may have changed');
}

Type guard

function hasListings(html) {
  return typeof html === 'string' && /<a\s[^>]*href="[^"]*\/(buy\/)?detail/i.test(html);
}

Try / catch

try {
  const rows = await guaziBrowse({ city });
} catch (e) {
  if (/No SSR listing anchors/.test(e.message)) {
    console.warn('Guazi layout may have changed; check m.guazi.com in a browser');
    return [];
  }
  throw e;
}

Prevention

When it happens

Trigger: Running 'guazi browse --city <code>' when Guazi's mobile page (/<code>/buy/) renders no <a> listing anchors — e.g. the site switched to client-side rendering, changed anchor markup/classes, or the city code resolves to a page with no inventory and an unexpectedly empty SSR body.

Common situations: Guazi front-end redesign or A/B test removing server-rendered anchors; scraping from a region where the mobile layout differs; city code pointing to a city with no used-car listings; bot-detection serving a JS-only shell page.

Related errors


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