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

  1. Remove the `proxy` key when using proxy_rotator
  2. Or drop proxy_rotator and keep the single static proxy
  3. 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

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


AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14). Data as JSON: /api/errors/e627253f96614550. Report an issue: GitHub.