Replace shared _is_retry flag from HttpxHooks with per-request retry checking using response.request.extensions.get("x402_is_retry")
#399
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.
Description
This PR fixes a critical concurrency issue in the
HttpxHooksclass that prevented concurrent 402 Payment Required responses from being processed correctly.The issue was caused by a shared
_is_retryflag that created a race condition - when multiple requests hit 402 responses concurrently, only the first request would perform the payment flow while all others would short-circuit and return 402 without attempting payment.The fix replaces the shared instance variable with per-request state using
httpx.Request.extensions, ensuring each request independently tracks its retry status without interfering with concurrent requests.Key Changes:
_is_retryinstance variable fromHttpxHooksclassrequest.extensions["x402_is_retry"]This enables critical use cases like:
Closes: #398
Tests
New Test Added
Added
test_concurrent_payment_requests()that verifies 5 concurrent payment requests all succeed independently.Test Results
All tests pass including the new concurrency test:
Updated Tests
test_on_response_retry()to use per-request extensions instead of shared flag_is_retryflag assertions from error handling testsChecklist
uv run ruff check src/x402/clients/httpx.py- All checks passed