unclecode/crawl4ai · warning · Error
Container not found: ${config.container_selector}
Error message
Container not found: ${config.container_selector} What it means
A 400 Bad Request returned by the streaming crawl endpoint when the request enables a deep crawl strategy (`deep_crawl_strategy` is set on crawler_config) but supplies more (or fewer) than one start URL. Deep-crawl streaming expands one start URL into a tree of links, so the implementation only supports a single seed per streamed request.
Source
Thrown at crawl4ai/async_crawler_strategy.py:1324
# Import VirtualScrollConfig to avoid circular import
from .async_configs import VirtualScrollConfig
# Ensure config is a VirtualScrollConfig instance
if isinstance(config, dict):
config = VirtualScrollConfig.from_dict(config)
self.logger.info(
message="Starting virtual scroll capture for container: {selector}",
tag="VSCROLL",
params={"selector": config.container_selector}
)
# JavaScript function to handle virtual scroll capture
virtual_scroll_js = """
async (config) => {
const container = document.querySelector(config.container_selector);
if (!container) {
throw new Error(`Container not found: ${config.container_selector}`);
}
// List to store HTML chunks when content is replaced
const htmlChunks = [];
let previousHTML = container.innerHTML;
let scrollCount = 0;
// Determine scroll amount
let scrollAmount;
if (typeof config.scroll_by === 'number') {
scrollAmount = config.scroll_by;
} else if (config.scroll_by === 'page_height') {
scrollAmount = window.innerHeight;
} else { // container_height
scrollAmount = container.offsetHeight;
}
// Perform scrollingView on GitHub (pinned to 7e80152142)
Solutions
- Send exactly one start URL per streaming deep-crawl request
- For multiple URLs, either issue one stream request per URL or drop deep_crawl_strategy and use the plain streaming batch mode
- For non-streaming deep crawl of several seeds, use the non-stream endpoint if your deployment supports it there
Example fix
# before
await stream_crawl(urls=["https://a.com", "https://b.com"],
crawler_config={"deep_crawl_strategy": BFSDeepCrawlStrategy()})
# after
for url in ["https://a.com", "https://b.com"]:
async for ev in stream_crawl(urls=[url],
crawler_config={"deep_crawl_strategy": BFSDeepCrawlStrategy()}):
... Defensive patterns
Strategy: validation
Validate before calling
def validate_stream_request(urls, crawler_config):
if crawler_config.get("deep_crawl_strategy"):
if len(urls) != 1:
raise ValueError("deep-crawl streaming requires exactly 1 URL")
return True Try / catch
try:
async for ev in stream_crawl(urls, crawler_config): ...
except HTTPException as e:
if e.status_code == 400 and "exactly one URL" in e.detail:
for u in urls: # fall back to one stream per URL
async for ev in stream_crawl([u], crawler_config): ...
else:
raise Prevention
- Hard-code the rule: deep crawl + stream => single seed URL per request
- Fan out multi-URL deep crawls as one request per URL
- Assert len(urls)==1 in your client wrapper before hitting the endpoint
When it happens
Trigger: POSTing to the stream endpoint with crawler_config containing a deep_crawl_strategy (e.g. BFSDeepCrawlStrategy) while `urls` in the payload has 0 or 2+ entries. The check `len(urls) != 1` fires before any browser is acquired.
Common situations: Reusing a batch-crawl payload (many URLs) against /crawl/stream after adding deep_crawl settings; enabling deep crawl in shared config and forgetting the endpoint takes exactly one seed URL; passing an empty urls list.
Related errors
- str(e)
- error_msg
- str(e)
- Invalid status: {status}. Must be one of: all, active, compl
- Invalid limit: {limit}. Must be between 1 and 1000
AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14).
Data as JSON: /api/errors/07d8f7204212e79a.
Report an issue: GitHub.