{"record":{"id":"d0ca348984589834","repo":"jackwener/OpenCLI","slug":"autohome-contexthint-http-resp-status","errorCode":null,"errorMessage":"autohome ${contextHint} HTTP ${resp.status}","messagePattern":"autohome (.+?) HTTP (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/autohome/utils.js","lineNumber":140,"sourceCode":"    return value;\n}\n\n/** Fetch an Autohome page as text. The grade + koubei pages are UTF-8. */\nexport async function ahFetch(url, contextHint) {\n    let resp;\n    try {\n        resp = await fetch(url, {\n            headers: {\n                'User-Agent': UA,\n                Referer: `${AH_BASE}/`,\n                'Accept-Language': 'zh-CN,zh;q=0.9',\n            },\n        });\n    } catch (err) {\n        throw new CommandExecutionError(`autohome ${contextHint} network error: ${err?.message || err}`);\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`autohome ${contextHint} HTTP ${resp.status}`);\n    }\n    return resp.text();\n}\n\n/** Extract __NEXT_DATA__ pageProps from a koubei page (pure, testable). */\nexport function extractPageProps(html) {\n    const m = String(html || '').match(/<script id=\"__NEXT_DATA__\"[^>]*>([\\s\\S]*?)<\\/script>/);\n    if (!m) return null;\n    try {\n        const data = JSON.parse(m[1]);\n        return (data && data.props && data.props.pageProps) || null;\n    } catch {\n        return null;\n    }\n}\n\nexport { ArgumentError, CommandExecutionError, EmptyResultError };\n","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/autohome/utils.js#L122-L158","documentation":"ahFetch() throws this CommandExecutionError when the server responded but with a non-2xx status. The context hint identifies which autohome page failed; the status code distinguishes 403 (bot-blocking), 404 (bad id/url), 429 (rate limit), 5xx (server-side) etc.","triggerScenarios":"Fetching a page with an invalid/deleted series id (404); autohome returning 403/429 to non-browser-like or too-frequent requests; transient 5xx outages.","commonSituations":"Scraping in a tight loop without delays; expired or geo-blocked pages; autohome WAF challenging the client.","solutions":["Read the status: 404 means the id/URL is wrong — verify the series id; 403/429 means throttling/bot detection — slow down, add delays, use better headers/cookies","Retry with exponential backoff for 5xx","Confirm the URL is current (autohome may have changed page paths)"],"exampleFix":"// before\nfor (const id of ids) await ahFetch(url(id), 'grade');\n// after\nfor (const id of ids) {\n  await ahFetch(url(id), 'grade');\n  await sleep(1500); // avoid 403/429\n}","handlingStrategy":"try-catch","validationCode":"const resp = await fetch(url, { method: 'HEAD' });\nif (resp.status === 404) throw new Error('Series page does not exist: ' + url);\nif (resp.status === 403 || resp.status === 429) throw new Error('Throttled by autohome');","typeGuard":null,"tryCatchPattern":"try {\n  const html = await ahFetch(url, 'grade');\n} catch (err) {\n  const m = /HTTP (\\d+)/.exec(err.message || '');\n  if (m) {\n    const status = Number(m[1]);\n    if (status === 404) throw new Error('Invalid series id/url');\n    if (status === 403 || status === 429) await sleep(backoff(attempt++));\n    else if (status >= 500) await sleep(backoff(attempt++));\n    return fetchWithRetry(url, attempt);\n  }\n  throw err;\n}","preventionTips":["Throttle requests (1-2s delay) to avoid 403/429 bot detection","Validate series ids exist before batch-fetching","Reuse browser-like headers/cookies and back off on 5xx"],"tags":["network","http","rate-limit"],"backgroundTag":"http-error-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}