jackwener/OpenCLI · error · CommandExecutionError
autohome brand catalog returned an unexpected HTML shape; ex
Error message
autohome brand catalog returned an unexpected HTML shape; expected brand <dl> blocks.
What it means
CommandExecutionError thrown by parseBrandSeries when the Autohome brand catalog HTML contains no <dl> blocks at all. Since the parser relies on <dl> blocks holding brand/series data, their absence means the fetched page is not the expected catalog (error page, anti-bot interstitial, or redesign).
Source
Thrown at clis/autohome/brand.js:35
BRAND_COLUMNS,
CommandExecutionError,
EmptyResultError,
ahFetch,
clean,
requireLimit,
requireStableId,
requireText,
resolveBrandInitial,
} from './utils.js';
/**
* Pure parser: catalog HTML + brand name → series rows. Exported for tests.
*/
export function parseBrandSeries(html, brandName, limit) {
const source = String(html || '');
const blocks = source.match(/<dl[^>]*>[\s\S]*?<\/dl>/g);
if (!blocks) {
throw new CommandExecutionError('autohome brand catalog returned an unexpected HTML shape; expected brand <dl> blocks.');
}
const want = String(brandName || '').replace(/[·\s]/g, '');
// No brand name (single-letter catalog mode): scan the whole page.
// Otherwise isolate the <dl> block whose <dt> names the brand.
let block = html;
if (want) {
block = null;
for (const b of blocks) {
const nameM = b.match(/<dt>[\s\S]*?<div>\s*<a[^>]*>([^<]+)<\/a>/);
const name = nameM ? clean(nameM[1]).replace(/[·\s]/g, '') : '';
if (name && (name === want || name.startsWith(want) || want.startsWith(name))) {
block = b;
break;
}
}
if (!block) return [];
}View on GitHub (pinned to 49907e53dc)
Solutions
- Retry later — the page shape may be temporarily altered by anti-bot protection.
- Slow request rate or change network/IP to avoid the anti-bot interstitial.
- Check the actual HTML returned (curl the URL) to confirm whether Autohome changed markup, then update the parser regex.
- Verify the catalog letter URL (e.g. /grade/carhtml/A.html) is still valid in a browser.
Example fix
// before
const rows = parseBrandSeries(errorPageHtml, '宝马', 10); // throws
// after: check response sanity before parsing
const html = await ahFetch(url, 'brand A');
if (!/<dl[\s\S]*<\/dl>/.test(html)) throw new Error('unexpected autohome response, got: ' + html.slice(0, 200));
const rows = parseBrandSeries(html, '宝马', 10); Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check HTML before parsing
if (!/<dl[\s\S]*<\/dl>/.test(html)) throw new Error('autohome page did not contain brand <dl> blocks'); Type guard
function hasBrandBlocks(html) {
return typeof html === 'string' && /<dl[^>]*>[\s\S]*?<\/dl>/.test(html);
} Try / catch
try {
const rows = await brand(brandArg, limit);
} catch (e) {
if (/unexpected HTML shape/.test(e.message)) {
// anti-bot page or markup change; back off and retry, or surface a clear failure
await sleep(5000);
return retryOnce();
}
throw e;
} Prevention
- Throttle requests to autohome and reuse sessions to reduce anti-bot pages.
- Detect captcha/verification markers in fetched HTML before parsing.
- Pin tests against a saved snapshot of real catalog HTML to catch markup changes.
- Fail loudly with a snippet of the unexpected HTML for diagnosis.
When it happens
Trigger: ahFetch returned HTML for /grade/carhtml/<letter>.html without any <dl> tags — e.g. an anti-bot/captcha page, a 404/soft-404 page, or Autohome changing the catalog markup.
Common situations: Autohome serving a verification page to datacenter IPs or after rapid requests; site redesign removing <dl> blocks; the letter page URL no longer existing.
Related errors
- No koubei data found — the series id may be wrong, or Autoho
- github-trending parser drift: missing ${field} for ${repo}
- github-trending parser drift: no repository rows found
- github-trending parser drift: missing repository link
- guazi browse ${code}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/478994235b225ddd.
Report an issue: GitHub.