D4Vinci/Scrapling · error · TypeError

Invalid proxy type: {type(proxy)}. Expected str or dict.

Error message

Invalid proxy type: {type(proxy)}. Expected str or dict.

What it means

Raised during ProxyRotator construction when an element of the `proxies` list is neither a str nor a dict. The rotator iterates the list and type-checks every entry; any other type (None, tuple, nested list, object) is rejected with the offending type in the message.

Source

Thrown at scrapling/engines/toolbelt/proxy_rotation.py:84

        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 = 0

    def get_proxy(self) -> ProxyType:
        """Get the next proxy according to the rotation strategy."""
        with self._lock:
            proxy, self._current_index = self._strategy(self._proxies, self._current_index)
            return proxy

    @property
    def proxies(self) -> List[ProxyType]:
        """Get a copy of all configured proxies."""
        return list(self._proxies)

    def __len__(self) -> int:
        """Return the total number of configured proxies."""
        return len(self._proxies)

View on GitHub (pinned to 5d213a2d47)

Solutions

  1. Make every entry a proxy URL string or a Playwright-style dict.
  2. Filter the pool first: pool = [p for p in pool if isinstance(p, (str, dict)) and p].
  3. Fix the parser that produced the pool so blank/malformed lines are dropped instead of yielded as None.

Example fix

# before
pool = load_lines('proxies.txt')  # contains '' and None entries
rotator = ProxyRotator(pool)

# after
pool = [line.strip() for line in open('proxies.txt') if line.strip()]
rotator = ProxyRotator(pool)
Defensive patterns

Strategy: type-guard

Validate before calling

clean_pool = [p for p in pool if isinstance(p, (str, dict)) and p]
if len(clean_pool) != len(pool):
    log.warning("dropped %d malformed proxy entries", len(pool) - len(clean_pool))
rotator = ProxyRotator(proxies=clean_pool)

Type guard

def is_pool_entry(value: object) -> bool:
    return isinstance(value, (str, dict))

Try / catch

try:
    rotator = ProxyRotator(proxies=pool)
except TypeError as e:
    if "Invalid proxy type" in str(e):
        pool = [p for p in pool if isinstance(p, (str, dict)) and p]
        rotator = ProxyRotator(proxies=pool)
    else:
        raise

Prevention

When it happens

Trigger: ProxyRotator(proxies=[None]), proxies=[('http', 'p', 8080)], proxies=[["http://p:8080"]] (nested list), or a pool containing a raw config object.

Common situations: Placeholder entries from parsing blank lines in a proxy file; mixed data from JSON that deserializes tuples as lists; optional proxy slots filled with None.

Related errors


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