From 7141648d3560526bb65c62299316b11a10dc02bf Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 31 May 2026 23:13:19 +0300 Subject: [PATCH] fix(browser): pageError.location needs valid defaults, not just optional chaining (#918 follow-up) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #918 patched pageError.location.{url,lineNumber,columnNumber} -> optional chaining (?.) but that is INSUFFICIENT. With location undefined, ?.url yields undefined, and playwright 1.60.0's protocol validator (tString) requires a string -> 'ValidationError: location.url: expected string, got undefined' -> still crashes the driver connection -> tradein-browser 500 + browser relaunch. The #918 fix merely traded TypeError for ValidationError; run_34 'verify' grepped for the TypeError signature and missed the ValidationError. Proven on the local stack (2026-05-31): once the proxy IP gets flagged, avito serves an anti-bot challenge page whose JS throws a locationless uncaught error on EVERY fetch -> ValidationError crash-storm -> 0 lots across all anchors. Fix: provide valid defaults so the validator passes — url -> '', line/column -> 0. The pageError event is then delivered without crashing; the browser returns the challenge HTML, the pipeline detects the firewall and rotates the IP gracefully instead of crash-looping. Verified in-container: node --check OK, 2 sites guarded. Refs #883, #901, #915, #916 --- tradein-mvp/browser/Dockerfile | 43 ++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/tradein-mvp/browser/Dockerfile b/tradein-mvp/browser/Dockerfile index e8239d08..d98b761c 100644 --- a/tradein-mvp/browser/Dockerfile +++ b/tradein-mvp/browser/Dockerfile @@ -23,26 +23,33 @@ WORKDIR /app # camoufox[geoip] тянет playwright как зависимость RUN pip install --no-cache-dir "camoufox[geoip]" aiohttp -# ── #915: null-guard для pageError.location в playwright 1.60.0 ───────────────── +# ── #915/#918: null-guard + ВАЛИДНЫЕ дефолты для pageError.location (playwright 1.60.0) ─ # Firefox/juggler-бэкенд иногда отдаёт uncaught JS-ошибку страницы (реклама / -# cross-origin скрипты на avito) БЕЗ поля location. playwright-диспетчер при -# доставке pageError-события читает `pageError.location.url` (coreBundle.js, -# 2 места: BrowserContextDispatcher + trace event) без null-guard → -# `TypeError: Cannot read properties of undefined (reading 'url')` ВНУТРИ -# event-хэндлера node-драйвера → рвёт protocol-соединение → "Connection closed -# while reading from the driver" / TargetClosedError → server.py отдаёт 500 и -# делает полный relaunch браузера (дорого, тормозит sweep). Недетерминированно: -# зависит от того, кинет ли конкретная загрузка locationless-ошибку. -# Апгрейд playwright невозможен — camoufox 0.4.11 pinned под конкретный -# Firefox+juggler. Чиним optional-chaining'ом прямо в pinned-бандле: -# `pageError.location.X` → `pageError.location?.X` (no-op когда location есть; -# отдаёт {url:undefined,...} когда нет → событие доставляется без краша). -# Сборка падает (exit 1) если паттерн не найден — сигнал что bump playwright -# сломал патч. +# cross-origin скрипты / anti-bot challenge на avito) БЕЗ поля location. +# playwright при доставке pageError-события собирает +# { url: pageError.location.url, line: ...lineNumber, column: ...columnNumber } +# (coreBundle.js, 2 места: BrowserContextDispatcher + trace event). +# +# Двухслойный баг (оба рвут protocol-соединение → "Connection closed while reading +# from the driver" / TargetClosedError → server.py 500 + relaunch браузера): +# 1. без guard → `pageError.location.url` бросает +# `TypeError: Cannot read properties of undefined (reading 'url')`; +# 2. ОДНОГО optional-chaining (`?.url`) НЕДОСТАТОЧНО (это была дыра #918): +# url станет undefined, а protocol-валидатор `tString` требует string → +# `ValidationError: location.url: expected string, got undefined`. +# Поэтому даём валидные дефолты: url → '', line/column → 0 — проходят валидатор, +# событие доставляется без краша. Профит: browser возвращает HTML challenge- +# страницы вместо краша → pipeline видит firewall и штатно ротирует IP, а не +# уходит в crash-storm (подтверждено прод-дебагом локального стека 2026-05-31). +# +# Апгрейд playwright невозможен — camoufox 0.4.11 pinned под Firefox+juggler. +# Сборка падает (exit 1), если паттерн не найден — сигнал что bump playwright сломал патч. RUN PW_BUNDLE="$(python -c 'import os,playwright;print(os.path.join(os.path.dirname(playwright.__file__),"driver","package","lib","coreBundle.js"))')" \ - && sed -i -E 's/pageError\.location\.(url|lineNumber|columnNumber)/pageError.location?.\1/g' "$PW_BUNDLE" \ - && N="$(grep -cF 'pageError.location?.url' "$PW_BUNDLE")" \ - && echo "tradein-browser: patched pageError.location?.url sites: $N" \ + && sed -i -E "s/pageError\.location\.url,/pageError.location?.url ?? '',/g" "$PW_BUNDLE" \ + && sed -i -E 's/pageError\.location\.lineNumber,/pageError.location?.lineNumber ?? 0,/g' "$PW_BUNDLE" \ + && sed -i -E 's/pageError\.location\.columnNumber/pageError.location?.columnNumber ?? 0/g' "$PW_BUNDLE" \ + && N="$(grep -cF "pageError.location?.url ?? ''" "$PW_BUNDLE")" \ + && echo "tradein-browser: patched pageError.location?.url ?? '' sites: $N" \ && [ "$N" -ge 2 ] COPY server.py ./server.py -- 2.45.3