D4Vinci/Scrapling · error · ValueError
Cannot use 'proxy_rotator' together with 'proxy'. Use either
Error message
Cannot use 'proxy_rotator' together with 'proxy'. Use either a static proxy or proxy rotation, not both.
What it means
PlaywrightConfig.__post_init__ rejects configurations that set both `proxy` (a single static proxy) and `proxy_rotator` (an object yielding a proxy per request). The two mechanisms are mutually exclusive because the rotator would be silently ignored with a static proxy present.
Source
Thrown at scrapling/engines/_browsers/_validators.py:103
cdp_url: Optional[str] = None
useragent: Optional[str] = None
extra_flags: Optional[List[str]] = None
blocked_domains: Optional[Set[str]] = None
block_ads: bool = False
retries: RetriesCount = 3
retry_delay: Seconds = 1
capture_xhr: str | None = None
executable_path: Optional[str] = None
dns_over_https: bool = False
def __post_init__(self): # pragma: no cover
"""Custom validation after msgspec validation"""
if self.page_action and not callable(self.page_action):
raise TypeError(f"page_action must be callable, got {type(self.page_action).__name__}")
if self.page_setup and not callable(self.page_setup):
raise TypeError(f"page_setup must be callable, got {type(self.page_setup).__name__}")
if self.proxy and self.proxy_rotator:
raise ValueError(
"Cannot use 'proxy_rotator' together with 'proxy'. "
"Use either a static proxy or proxy rotation, not both."
)
if self.proxy:
self.proxy = construct_proxy_dict(self.proxy)
if self.cdp_url:
cdp_msg = _is_invalid_cdp_url(self.cdp_url)
if cdp_msg:
raise ValueError(cdp_msg)
if not self.cookies:
self.cookies = []
if not self.extra_flags:
self.extra_flags = []
if not self.selector_config:
self.selector_config = {}
if not self.additional_args:
self.additional_args = {}View on GitHub (pinned to 5d213a2d47)
Solutions
- Remove the `proxy` key when using proxy_rotator
- Or drop proxy_rotator and keep the single static proxy
- If you need a one-off static override per request, use fetch(url, proxy=...) which overrides the rotator per-call by design
Example fix
// before session = StealthySession(proxy='http://u:p@host:8080', proxy_rotator=ProxyRotator([...])) // after session = StealthySession(proxy_rotator=ProxyRotator([...])) # per-call static override: resp = session.fetch(url, proxy='http://u:p@host:8080')
Defensive patterns
Strategy: validation
Validate before calling
if cfg.get('proxy') and cfg.get('proxy_rotator'):
del cfg['proxy'] # rotator wins; or drop the rotator instead
session = StealthySession(**cfg) Type guard
def proxy_config_is_consistent(proxy, rotator) -> bool:
return not (proxy and rotator) Try / catch
try:
session = StealthySession(proxy=p, proxy_rotator=r)
except ValueError as e:
if 'proxy_rotator' in str(e):
session = StealthySession(proxy_rotator=r)
else:
raise Prevention
- Keep exactly one of proxy / proxy_rotator in any config source
- Assert consistency when merging config dicts from multiple sources
- Use fetch(url, proxy=...) for intentional per-call static overrides
When it happens
Trigger: Creating StealthySession/PlaywrightSession (or a fetch call) with both proxy='http://...' and proxy_rotator=ProxyRotator(...) in the same config.
Common situations: Migrating from a static proxy to rotation and forgetting to remove the old proxy key; merging config dicts where both keys survive.
Related errors
- page_action must be callable, got {type(self.page_action).__
- page_setup must be callable, got {type(self.page_setup).__na
- CDP URL must use 'ws://', 'wss://', 'http://', or 'https://'
- Init script path not found: {value}
- Invalid argument type: {e}
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/e627253f96614550.
Report an issue: GitHub.