D4Vinci/Scrapling · error · TypeError
strategy must be callable, got {type(strategy).__name__}
Error message
strategy must be callable, got {type(strategy).__name__} What it means
Raised by `ProxyRotator.__init__` when the `strategy` argument is not callable. The rotation strategy is a function taking (proxies, current_index) and returning (proxy, next_index); passing anything that cannot be called (a string name, a number, None) fails this check.
Source
Thrown at scrapling/engines/toolbelt/proxy_rotation.py:68
def __init__(
self,
proxies: List[ProxyType],
strategy: RotationStrategy = cyclic_rotation,
):
"""
Initialize the proxy rotator.
:param proxies: List of proxy URLs or Playwright-style proxy dicts.
- String format: "http://proxy1:8080" or "http://user:pass@proxy:8080"
- Dict format: {"server": "http://proxy:8080", "username": "user", "password": "pass"}
:param strategy: Rotation strategy function. Takes (proxies, current_index) and returns (proxy, next_index). Defaults to cyclic_rotation.
"""
if not proxies:
raise ValueError("At least one proxy must be provided")
if not callable(strategy):
raise TypeError(f"strategy must be callable, got {type(strategy).__name__}")
self._strategy = strategy
self._lock = Lock()
# Validate and store proxies
self._proxies: List[ProxyType] = []
self._proxy_to_index: Dict[str, int] = {} # O(1) lookup by unique key (server + username)
for i, proxy in enumerate(proxies):
if isinstance(proxy, (str, dict)):
if isinstance(proxy, dict) and "server" not in proxy:
raise ValueError("Proxy dict must have a 'server' key")
self._proxy_to_index[_get_proxy_key(proxy)] = i
self._proxies.append(proxy)
else:
raise TypeError(f"Invalid proxy type: {type(proxy)}. Expected str or dict.")
self._current_index = 0View on GitHub (pinned to 5d213a2d47)
Solutions
- Pass the function object itself: from scrapling.engines.toolbelt.proxy_rotation import random_rotation; ProxyRotator(proxies, strategy=random_rotation).
- If config gives a string, map it to the function first: strategies = {'random': random_rotation, 'cyclic': cyclic_rotation}.
- Do not add parentheses after the strategy name when passing it.
Example fix
# before rotator = ProxyRotator(proxies, strategy='random_rotation') # after from scrapling.engines.toolbelt.proxy_rotation import random_rotation rotator = ProxyRotator(proxies, strategy=random_rotation)
Defensive patterns
Strategy: type-guard
Validate before calling
assert callable(strategy), f"strategy must be a function, got {type(strategy).__name__}"
rotator = ProxyRotator(proxies=pool, strategy=strategy) Type guard
from typing import Any, Callable
def is_rotation_strategy(value: Any) -> bool:
return callable(value) and not isinstance(value, (str, bytes)) Try / catch
try:
ProxyRotator(pool, strategy=strategy)
except TypeError as e:
if "strategy must be callable" in str(e):
from scrapling.engines.toolbelt.proxy_rotation import cyclic_rotation
strategy = cyclic_rotation # safe default
else:
raise Prevention
- Pass function objects, never names or results (no parentheses).
- Map config strings to functions via a dict lookup.
- Test custom strategies with two-element pools in unit tests.
When it happens
Trigger: ProxyRotator(proxies=pool, strategy='random') (passing the name instead of a function), strategy=None, or strategy=random_rotation() (calling the function and passing its result instead of the function itself).
Common situations: Config files storing the strategy as a string that is passed through verbatim; accidentally invoking the strategy with () at the call site; copy-pasting example code that instantiates instead of references.
Related errors
- Invalid proxy type: {type(proxy)}. Expected str or dict.
- Response meta should be dictionary but got {type(meta).__nam
- Invalid proxy string: {proxy_string}
- At least one proxy must be provided
- Proxy dict must have a 'server' key
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/6b2f2be2f07033ad.
Report an issue: GitHub.