unclecode/crawl4ai · error · HTTPStatusError
Unexpected status code for {url}
Error message
Unexpected status code for {url} What it means
Raised by the HTTP-mode crawler when the server responds with a status outside 200-299. It raises HTTPStatusError carrying the numeric status and the URL. The response body has already been read (raw_bytes), but no HTML/AsyncCrawlResponse is produced — non-2xx is treated as failure, with no built-in retry.
Source
Thrown at crawl4ai/async_crawler_strategy.py:2724
if config.proxy_config:
proxy_url = self._format_proxy_url(config.proxy_config)
request_kwargs['proxy'] = proxy_url
if self.browser_config.method == "POST":
if self.browser_config.data:
request_kwargs['data'] = self.browser_config.data
if self.browser_config.json:
request_kwargs['json'] = self.browser_config.json
await self.hooks['before_request'](url, request_kwargs)
try:
async with session.request(self.browser_config.method, url, **request_kwargs) as response:
raw_bytes = await response.read()
content = memoryview(raw_bytes)
if not (200 <= response.status < 300):
raise HTTPStatusError(
response.status,
f"Unexpected status code for {url}"
)
response_headers = dict(response.headers)
content_type = response.content_type or 'text/html'
content_type = content_type.split(';')[0].strip().lower()
content_disposition = response_headers.get('Content-Disposition', '')
downloaded_files = None
html = ""
if self._is_file_download(content_type, content_disposition):
# Save file to disk
downloads_path = self.browser_config.downloads_path or os.path.join(
os.path.expanduser("~"), ".crawl4ai", "downloads"
)
os.makedirs(downloads_path, exist_ok=True)View on GitHub (pinned to 7e80152142)
Solutions
- Catch HTTPStatusError and branch on e.status: skip 404/410, retry 429/5xx with backoff honoring Retry-After.
- Set custom headers (User-Agent, cookies) via BrowserConfig(headers=...) since plain aiohttp is easily blocked.
- Reduce request rate / add delays when seeing 429.
- If you need the error page's HTML anyway, use the Playwright browser strategy instead of HTTP mode.
Example fix
// before
result = await crawler.arun(url)
// after
from crawl4ai.async_crawler_strategy import HTTPStatusError
try:
result = await crawler.arun(url)
except HTTPStatusError as e:
if e.status in (429, 502, 503):
await asyncio.sleep(5)
result = await crawler.arun(url)
else:
raise Defensive patterns
Strategy: try-catch
Validate before calling
async def url_ok(session, url: str) -> bool:
async with session.head(url, allow_redirects=True) as r:
return 200 <= r.status < 300 Try / catch
from crawl4ai.async_crawler_strategy import HTTPStatusError
try:
result = await crawler.arun(url)
except HTTPStatusError as e:
if e.status in (404, 410):
mark_gone(url)
elif e.status in (429, 502, 503):
await asyncio.sleep(5)
result = await crawler.arun(url)
else:
raise Prevention
- Send realistic User-Agent and headers in HTTP mode
- Honor Retry-After on 429
- Prune dead URLs from sitemaps periodically
When it happens
Trigger: Fetching a 404/410 page, 403 from bot protection (Cloudflare), 429 rate limiting, 500/502/503 from origin or proxy, or a redirect chain landing on an error page. Any arun() with AsyncHTTPCrawler/BrowserConfig that selects the HTTP strategy.
Common situations: Crawling at high request rates hitting 429s; sites requiring cookies/headers the plain aiohttp request lacks; stale URLs from a sitemap returning 404/410; proxies returning 502/503.
Related errors
- Request timed out: {str(e)}
- Connection failed: {str(e)}
- HTTP client error: {str(e)}
- HTTP request failed: {str(e)}
- Failed on navigating ACS-GOTO: {str(e)}
AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14).
Data as JSON: /api/errors/9b737f6ff9312c7d.
Report an issue: GitHub.