Skip to content

lab-ops-hub: container crawler records redirect loops and over-limit chains as successful 302 crawls — PR #573's blanket HTTPError catch swallows urllib redirect errors #576

Closed

Description

@dezren39

Summary

PR #573 (fixing #572) made the lab-ops-hub container crawler treat urllib.error.HTTPError as a crawl result, so target 4xx/5xx pages are extracted instead of becoming a 502. The catch is blanket, though: urllib also raises HTTPError for redirect failures — redirect loops (max_repeats) and chains past max_redirections in HTTPRedirectHandler.http_error_302 raise HTTPError carrying the 3xx code. _fetch_crawl now converts those into a "successful" result, so a redirect-looping URL is recorded as a completed crawl with status_code: 302 and the redirect stub's body as its text — the crawl no longer fails, and the Worker fallback (which would surface its own too-many-redirects error) never runs.

Evidence

  • PR: lab-ops-hub: container crawler returns error-status pages as results, not 502 #573 (merged, commit 4bb8903ca36519049e3bb81bb9383c0bbfaa0fb9, merge ef2f2efd)
  • pkgs/lab-ops-hub/container/app.py:412-430 on current main (ef2f2efd): _fetch_crawl catches urllib.error.HTTPError around _OPENER.open(...) with no status-code check and returns (status, body) for whatever it carries.
  • CPython urllib.request.HTTPRedirectHandler.http_error_302: when a redirect target was already visited >= max_repeats times or len(visited) >= max_redirections, it raises HTTPError(req.full_url, code, self.inf_msg + msg, headers, fp) — code is the 3xx (e.g. 302), not a 4xx/5xx.
  • Handler.do_POST (app.py:493) trusts _fetch_crawl's return and answers HTTP 200 with status_code set, so crawlWithContainer in src/index.ts accepts it as a container result (no fallback, source: container recorded in D1).

Reproduction (verified 2026-10-22 on current main ef2f2efd)

Scratch harness (outside the repo, validation patched to identity only because the validating redirect handler refuses 127.0.0.1 targets by design; production redirect targets are public URLs, the urllib mechanics are identical):

Why it matters

A redirect loop or runaway chain is a crawl failure, and both crawlers used to surface it as one (container: 502 → Worker fallback → the fallback's too many redirects error). Now the container silently manufactures a plausible-looking result from the redirect response itself: D1/R2 record a completed crawl whose title/text/links are extracted from a redirect stub page, with status_code: 302. Nothing downstream can tell this apart from a genuine 302 page crawl.

Related but distinct: #571 tracks aligning the two crawlers' redirect policy (hop limit, followed codes, non-followed 3xx handling). This issue is narrower and newer — #571 was filed against main 7a9ac873, where these errors still surfaced as a 502; #573 changed that failure mode into a silent success after #571 was written. Aligning the policy in #571 does not by itself require un-masking these errors, and un-masking them does not require settling the policy.

Acceptance criteria

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions