Skip to content

fix(fedex): session warm-up + JSON API interception to fix tracking failures#25

Merged
WolffRuoff merged 3 commits into
mainfrom
fix/fedex-tracking
Mar 11, 2026
Merged

fix(fedex): session warm-up + JSON API interception to fix tracking failures#25
WolffRuoff merged 3 commits into
mainfrom
fix/fedex-tracking

Conversation

@WolffRuoff
Copy link
Copy Markdown
Owner

Summary

FedEx tracking was failing with TargetClosedError during page.wait_for_selector — the browser context was being terminated before the page could render when hit cold.

Two-part fix in scraper/carriers/fedex.py:

  • Session warm-up: Visit fedex.com/en-us/home.html (domcontentloaded) before navigating to the tracking URL, establishing a realistic session with cookies and TLS fingerprint
  • JSON API interception: FedEx's SPA calls an internal /trackingCal/track endpoint to load tracking data. We now intercept that response during page.goto() and parse the JSON directly — skipping wait_for_selector entirely (the source of TargetClosedError). HTML parsing is retained as a fallback if the JSON response isn't captured.

Also adds _parse_tracking_json() to handle the intercepted JSON structure (TrackPackagesResponse.packageList[0]).

Test plan

  • make test-scraper — all 181 tests pass
  • TestParseTrackingJson — 7 new unit tests for JSON parsing (delivered, in-transit, edge cases)
  • TestAsyncTrack updated — verifies two goto calls, JSON path skips wait_for_selector, HTML fallback still works
  • Manual: rebuild Docker image, add a FedEx package, call POST /api/packages/{number}/refresh, confirm non-unknown status returned

🤖 Generated with Claude Code

WolffRuoff and others added 3 commits March 11, 2026 16:18
…ot detection

FedEx tracking was failing with TargetClosedError during wait_for_selector.
The tracking page is fronted by a bot protection service that terminates the
browser context before rendering when hit cold.

Two-part fix:
- Visit the FedEx homepage (domcontentloaded) before the tracking URL to
  establish a real-looking session with cookies/TLS fingerprint
- Intercept the internal /trackingCal/track JSON API response fired during
  page navigation and parse it directly, skipping wait_for_selector entirely
- HTML parsing retained as a fallback if the JSON response is not captured

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three fixes discovered during live testing:

1. Tracking API is at api.fedex.com/track/v2/shipments (not /trackingCal/track)
   — update URL filter to match "api.fedex.com/track/" instead of "trackingCal"

2. API response JSON uses output.packages[0] structure with mainStatus/
   estDeliveryDt/scanEventList fields — rewrite _parse_tracking_json accordingly

3. Race condition: API response fires DURING wait_for_selector (not during goto),
   so using wait_for_selector or page.wait_for_response as a blocking call
   misses it. Fix: use asyncio.Event set by _on_response; asyncio.wait_for
   races the event against a 45s timeout, then parses JSON if captured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@WolffRuoff WolffRuoff merged commit 9be324d into main Mar 11, 2026
5 checks passed
@WolffRuoff WolffRuoff deleted the fix/fedex-tracking branch March 11, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant