Errors, Status Codes & Cache Discipline
A failed crawl is still a result. Never touch result.markdown until result.success is true.
Concept
Network timeouts, 403/429/500 responses, robots.txt blocks, and browser launch failures all surface through result.success and result.error_message. The status_code field tells you the HTTP status, while redirected_status_code captures the final status after redirects. result.url is always the final URL.
Cache discipline matters during development. CacheMode.BYPASS fetches a fresh page, which is essential when iterating on selectors or verifying fixes. CacheMode.ENABLED reads from cache when available and writes fresh responses when not. Use READ_ONLY when you want to avoid polluting the cache, and WRITE_ONLY when you want to refresh cache entries without reading them.
Respect robots.txt by setting check_robots_txt=True on allowed targets, and stay within rate limits and terms of service. A respectful crawler is a repeatable crawler.
Use cases
- Detect and log 404s in a URL inventory before sending them to extraction.
- Retry a failed crawl with
CacheMode.BYPASSafter fixing a selector. - Honor robots.txt when crawling a site you do not own.
Video walkthrough
Code example
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
urls = [
"https://example.com",
"https://httpbin.org/status/404",
"https://httpbin.org/redirect/1",
]
async with AsyncWebCrawler() as crawler:
for url in urls:
result = await crawler.arun(
url=url,
config=CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
check_robots_txt=True,
),
)
print(f"URL: {url}")
print(f" success: {result.success}")
print(f" final_url: {result.url}")
print(f" status: {result.status_code}")
if not result.success:
print(f" error: {result.error_message}")
print()
asyncio.run(main())
Practice lab
Practice lab
Objective: Crawl a mix of good, missing, and redirecting URLs and report the outcomes.
Steps
- Create a list of three URLs: one known good page, one 404 page, and one redirecting page.
- Loop over the list with
CacheMode.BYPASSandcheck_robots_txt=True. - For each result, print the original URL, final URL, success flag, status code, and error message if it failed.
- Count how many URLs succeeded and how many failed.
Success criteria
Expected output
Hints
https://httpbin.org/status/404reliably returns 404.https://httpbin.org/redirect/2follows two redirects.
Solution
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
async def main():
urls = [
"https://example.com",
"https://httpbin.org/status/404",
"https://httpbin.org/redirect/2",
]
async with AsyncWebCrawler() as crawler:
for url in urls:
result = await crawler.arun(
url=url,
config=CrawlerRunConfig(cache_mode=CacheMode.BYPASS),
)
print(f"{url} -> {result.url}")
print(f" success={result.success} status={result.status_code}")
if not result.success:
print(f" error={result.error_message}")
asyncio.run(main())
Common mistakes
CacheMode.BYPASS on in production, which disables useful caching.check_robots_txt=True results and crawling disallowed paths anyway.