jackwener/OpenCLI · error · CommandExecutionError

guazi ${contextHint} HTTP ${resp.status}

Error message

guazi ${contextHint} HTTP ${resp.status}

What it means

guaziFetch checks resp.ok after the request succeeds at the transport level and throws CommandExecutionError for any non-OK HTTP status (404, 403, 500, rate-limit 429, etc.), embedding the status code and contextHint. This distinguishes server-side rejections from network failures.

Source

Thrown at clis/guazi/utils.js:120

    return id;
}

/** Fetch a Guazi mobile page as HTML text, throwing typed errors. */
export async function guaziFetch(path, contextHint) {
    let resp;
    try {
        resp = await fetch(`${GUAZI_M_BASE}${path}`, {
            headers: {
                'User-Agent': UA,
                Referer: `${GUAZI_M_BASE}/`,
                'Accept-Language': 'zh-CN,zh;q=0.9',
            },
        });
    } catch (err) {
        throw new CommandExecutionError(`guazi ${contextHint} network error: ${err?.message || err}`);
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`guazi ${contextHint} HTTP ${resp.status}`);
    }
    const html = await resp.text();
    // Guazi may eventually push the mobile pages behind their JS challenge.
    if (/瑞数|reese84|captcha|滑动验证|verify\.guazi|安全验证/i.test(html) && !/car-detail\/c\d+/.test(html)) {
        throw new AuthRequiredError(
            'guazi.com',
            `guazi ${contextHint} hit an anti-bot challenge — Guazi may have started gating the mobile site.`,
        );
    }
    return html;
}

export { ArgumentError, CommandExecutionError, EmptyResultError };

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log resp.status from the error message and check it: 404 means bad id/path, 403/429 means blocked
  2. Add delays/backoff between requests to avoid rate-limiting
  3. Verify the car id or path is correct by opening it in a browser
  4. Rotate IP/proxy if consistently 403/429

Example fix

// before
await guaziFetch('/car/99999999999', 'car page'); // 404
// guazi car page HTTP 404

// after
const listing = await findLiveCarId(query); // resolve a valid id first
await guaziFetch(`/car/${listing.id}`, 'car page');
Defensive patterns

Strategy: retry

Validate before calling

// verify the target page exists before scraping
const HEAD = await fetch(`${GUAZI_M_BASE}${path}`, { method: 'HEAD' });
if (HEAD.status === 404) throw new Error('listing not found');

Type guard

const isHttpError = (e) => e instanceof CommandExecutionError && /HTTP \d+/.test(e.message);

Try / catch

try {
  const html = await guaziFetch(path, hint);
} catch (e) {
  const m = e.message.match(/HTTP (\d+)/);
  if (m && ['429','500','502','503'].includes(m[1])) return retryWithBackoff(() => guaziFetch(path, hint), 3);
  throw e;
}

Prevention

When it happens

Trigger: guaziFetch(path, contextHint) receiving a response with resp.ok === false: a deleted/invalid car listing (404), IP rate-limiting or WAF block (403/429), or Guazi server errors (5xx).

Common situations: Scraping a car id that no longer exists; hitting Guazi too frequently from one IP and getting rate-limited; the mobile endpoint path changed in a site redesign.

Related errors


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