Repository navigation
Latest transfers: stop counting a repeated page as served; read the edge's 502 as a failed page - #11
Merged
Merged
Conversation
…2 as a failed page On 2026-10-15 the default latest-transfers view (filtered by the site to players valued EUR 500,000+) held 20 transfers on one page with no pager, and ?page=2 served page 1 again. A page number past the end of the unfiltered listing is answered the same way, with the last page again. The run counted the repeat as a completed page and ended "complete". A page whose rows were all seen on earlier pages is now never counted. If the page before it had no next link the listing ended there (stop_reason listing_ended, exit 0, repeated_page / repeat_of_page / listing_pages in the sidecar); if it had one, a page was promised and not served (stop_reason page_repeated, exit 6). Same rule in all four entry points and in Playwright's --concurrency path, which also stops dispatching on a repeat. The edge's bare nginx 5xx page is recognised from its body. After the AWS WAF challenge clears, its status is gone, and a 502 left in the browser read as an empty page 1 while the run went on to report complete. The canary now also runs --mode transfers on the unfiltered listing and requires more than one page served, unique transfer ids and filled core fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f calling it blocked --mode transfers --min-value EUR (0 = no floor) reads the latest-transfers listing through its filtered path with minMarktwert set, keeping any filters a typed --url carries. Without the flag the site's own default view (filtered by the site to EUR 500,000+) is read as before. The sidecar records min_market_value_eur and market_value_filter (url / site_default). Negative, non-integer, or used with another --mode: exit 2. All four entry points. The canary's transfers run now uses --min-value 0 and checks the recorded floor. An HTTP 5xx from the site, or the edge's bare nginx 5xx page when no status is visible, is now a server error, not a block: the page is fetched again on the same exit up to 2 times (--retry-delay x1, x2; no rotation, no paid solve, under the page's one solve budget), then failed as target_http_5xx, exit 5 on page 1 and 6 later. A visible 5xx used to end exit 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On 2026-10-15,
--mode transfersread 20 rows. The page had no next link, and?page=2returned the same rows as page 1. The run still reportedpages_completed: 2,status: complete.I measured the listing that day with free requests: Playwright, system Chromium, no proxy, no key.
/statistik/neuestetransfersapplies the site's own market-value floor. Its filter form and tab links carryminMarktwert=500000. That day 20 transfers matched, which is less than one 25-row page. So the page had no pager and norel=next. That is the whole filtered listing./transfers/neuestetransfers/statistik?land_id=0&verein_land_id=0&wettbewerb_id=alle&minMarktwert=0&maxMarktwert=500000000had 4 pages: 25, 25, 25 and 2 rows, 77 transfers. Each page has a plain&page=Nnext link that keeps the filters. Pages 1 to 3 share no transfer ids.?page=2of the default view served page 1.?page=5,?page=401and?page=9999of the unfiltered view all served page 4. The pager still marked page 4 as current.502 Bad Gateway.empty.complete.Resulting behaviour
Repeated pages are not counted. A later page whose rows were all seen on earlier pages is never counted as completed, and its rows are dropped as duplicates. What it means depends on the page before it:
stop_reason: listing_ended, exit 0. The sidecar getsrepeated_page,repeat_of_pageandlisting_pages.stop_reason: page_repeated, exit 6,status: partial.Where the rule applies:
--concurrencypath. It decides afterwards in page order (page_flow.settle_repeats), and it stops dispatching once a page brings nothing new.Next links come from the HTML. Each page's next link is read from the served HTML by a shared helper (
product_parser.next_page_link), so every engine gives the same page the same answer.A bare nginx 5xx page is a failed page, never an empty one, in every engine, with or without a status.
Docs:
[Unreleased]has a behaviour-change note and two Fixed entries.Canary: added a
--mode transfers --pages 3run on the unfiltered URL. It turns red unless:stop_reasoniscompletedorlisting_ended;It runs even when the market-values checks failed. Its artifacts are uploaded.
Evidence
Live runs, 2026-10-15 (Playwright, system Chromium, no proxy, no key):
--mode transfers --pages 3(default view)listing_ended, 1 page counted, 20 rows,repeated_page: 2,repeat_of_page: 1--url <unfiltered> --pages 6listing_ended, 4 pages, 77 unique rows,repeated_page: 5,repeat_of_page: 4--concurrency 2status: completeOffline suite:
PYTHONDONTWRITEBYTECODE=1,__pycache__cleared before each run.ci_checks.py --all, pytest andpy_compileare all OK.New fixtures are real captures from that day, cut to the grid's first rows plus the pager, the head links and the keys div:
?page=2repeat;New checks:
PageSequencerule on its own;scrape()fed the captures: the one-page listing, the 3-page listing with a past-the-end page, and a planted case where page 1 links onward but page 2 serves page 1;_fetch_one_pagerecords the next link;Planted-fault controls. Each one turned the suite red on the named checks, and the suite was green again once restored:
--concurrency)listing_endednot complete--concurrencyskipssettle_repeatsno_new_products, 3 pages counted)Not changed
--mode transfersURL is still the site's own default view, with its EUR 500k floor. Switching the default to the unfiltered listing would change what the mode means, so that is left to the owner.blocked(exit 3 on page 1). A body-only 5xx now gets the same classification. A separate "server error" state with its own retry policy is not added here.Second commit:
--min-valueand a site 5xx is a server error (owner decision)--mode transfers --min-value EUR(0 = no floor) is available in all four entry points./transfers/neuestetransfers/statistik?land_id=0&verein_land_id=0&wettbewerb_id=alle&minMarktwert=EUR&maxMarktwert=500000000. Any filters a typed--urlalready carries are kept.--mode.min_market_value_eurandmarket_value_filter. The filter isurl, orsite_defaultwhen the floor is the site's own and not in the URL.--min-value 0instead of a hand-built URL, and checks the recorded floor.A target-site 5xx is a server error, not a block. This covers an HTTP 5xx, and the edge's 155-byte nginx 502 page when no status is visible.
--retry-delayx1 then x2 apart. A retry does not rotate the proxy, does not buy a solve and does not reset the page's single solve allowance.stop_reason: target_http_5xx, exit 5 on page 1 and exit 6 on a later page.Evidence for this commit
Real-code runs: each entry point's real
scrape()and real_fetch_one_pageran against a scripted site serving the real 502 capture. Only the browser or the Scraper API call was replaced (fake page objects for Playwright, pyppeteer and Selenium).target_http_5xx, 3 fetches.pages_failed: [2].Parse checks of
--min-valuein all four entry points: 0, 500000, -1, x, and the flag with market-values.Sidecar checks for
--min-value 0and for the default view.Live run (2026-10-16),
--mode transfers --min-value 0 --pages 6: exit 0,listing_ended, 4 pages, 78 rows,min_market_value_eur: 0. The default view was exit 0, 20 rows,market_value_filter: site_default.Offline suite: no failures in any venv.
ci_checks --alland pytest also pass.Planted-fault controls: each turned the suite red on the named checks, and the suite was green again once restored.
PageAttemptsnever repeatsfailure_reasonforgets 5xx--min-value--min-valueunbounded (pyppeteer)The "Not changed" items about the default URL and 5xx-as-blocked above are now resolved by this commit.
🤖 Generated with Claude Code