# Changelog of Njuškalo Car Scraper (Croatia) (`devilscrapes/njuskalo-croatia-cars`) Actor

- **URL**: https://apify.com/devilscrapes/njuskalo-croatia-cars/changelog.md
- **Full Actor documentation**: https://apify.com/devilscrapes/njuskalo-croatia-cars.md

## Njuškalo Car Scraper (Croatia) — Changelog

### 0.2.0 — 2026-08-26

- Fix: 80% customer success rate (7/36 30-day FAILED) traced to
  `_Fetcher._attempt` catching every `curl_cffi` transport error
  (`(OSError, RuntimeError)` — timeout, connection reset, DNS failure,
  proxy error, since `curl_cffi.curl.CurlError` subclasses `OSError`) and
  returning `(None, blocked=False)`, which skipped the retry loop
  entirely. A single flaky-proxy hiccup on page 1 of the search — a
  realistic event on a residential exit — was returned immediately with
  zero retries; `_iter_listings` then saw no listings on page 1 and
  raised `BlockedError`, failing the WHOLE run even though the target
  never actually blocked anything. This also silently violated the
  documented retry contract ("retry on 408/429/503 and network errors" —
  DEVILSCRAPES-CONTEXT.md § Anti-blocking stack), which the code's own
  docstring claimed to follow but did not implement for the network-error
  branch.
- Fix: transport errors, HTTP 403/408/429/503, and CAPTCHA challenges now
  all retry with backoff + a fresh proxy session (`RETRYABLE_STATUS_CODES`
  now includes 408; network errors return `retry=True` instead of
  `blocked=False`). Only a genuine non-retryable HTTP status (e.g.
  404/410 — listing gone) returns a soft `None` without exhausting
  retries.
- Verified live build has been deployed unchanged for the full 30-day
  failure window (`taggedBuilds.latest` finished 2026-06-07, 80 days
  before this fix) — the failures are on the shipped code, not a stale
  measurement window.
- Verified proxy-exit-per-attempt rotation (`ProxyFactory` /
  `new_proxy_url`) was already correct on this Actor — NOT the
  sticky-session bug fixed on `standvirtual-portugal-cars`. Confirmed via
  code read: `main.py::_build_proxy_factory` mints a fresh `session_id`
  per call and `scraper.py::_Fetcher._rotate` calls it on every retry.
- Verified geo (random-country vs `country-HR` residential exit) is NOT
  currently a failure cause: 8/8 random-country + 1/1 HR-pinned probe
  requests against `https://www.njuskalo.hr/auti` all returned 200 with
  a parseable `__INITIAL_STATE__` — no geo-splash reproduced. Left the
  proxy country unpinned; revisit if evidence emerges.
- Added `tests/test_network_retry.py` — regression test that fails on
  the old code (network error -> zero retries, immediate `None`/no
  `BlockedError`) and passes once transport errors are retried like any
  other block signal.

### 0.1 — 2026-06-05

- First cloud publish. Verified SUCCEEDED on Apify platform (runId=MfJ5fQjSOGELa6wCi).
- 5/5 rows populated with EUR prices, Croatian diacritics (š/ž/č) correct, enrichDetails=true working.
- PPE pricing to be activated separately.

### 0.1.0 — 2026-05-15

- Initial scaffolded release.
