avito_full_load has been failing since 2026-07-02 (8 runs status=failed, some
with error=''). Root cause: _fetch_serp_html_browser only caught
httpx.HTTPStatusError from BrowserFetcher.fetch. When the sidecar fetch fails
with httpx.TimeoutException/httpx.TransportError (ReadTimeout/ConnectError/
RemoteProtocolError) after BrowserFetcher's own internal retry, the exception
was unhandled and propagated through _paginate_incremental_bracket into
run_avito_full_load's generic `except Exception` -> mark_failed(str(exc)).
str(ReadTimeout())=="" produced an empty error, and mark_failed does not
persist done_buckets (unlike mark_banned), losing the resume checkpoint.
Widen the except clause to also catch httpx.TimeoutException/TransportError,
mirroring the existing pattern in _probe_total. Non-HTTPStatusError exceptions
have no HTTP status, so they're classified status=0 with a descriptive
error_text and routed through the existing transient-backoff branch (not
soft-ban — status=0/exception text never matches the soft-ban keyword checks).
Once the bounded retry budget is exhausted, they raise AvitoRateLimitedError,
same as a sidecar HTTPStatusError -> run_avito_full_load's dedicated
except (AvitoBlockedError, AvitoRateLimitedError) -> mark_banned, which DOES
persist done_buckets, giving graceful partial-save instead of a crash.
Does NOT fix the underlying cause (mobile proxy instability) -- that's
infra/devops, out of scope. This only makes the failure mode graceful.