๐Ÿ“… Historical page. This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits and no top-up rail. โ†’ See current

The Other 8 KiB: Your 402 Fits, but Can the Buyer's Retry Get Back In?

2026-11-09 ยท x402 operations

In October we wrote about the wall a buyer hits on the way in: a Python aiohttp client refuses any single header line over 8,190 bytes, so a PAYMENT-REQUIRED header that crosses it is never parsed. The endpoint looks healthy, the 402 is technically served, and nobody can pay it.

There is a second wall, and it has a different owner. The buyer reads the challenge, signs a payment payload, and sends it back in one PAYMENT-SIGNATURE header line โ€” into the seller's ingress. That ingress has its own per-line cap. It is not derived from the 402, and when the retry crosses it, nothing happens on the origin side at all: no handler, no delivery row, no log line of ours. The request is refused before it exists.

This post measures that return leg on our own deployment and gives the arithmetic you can apply to yours.

Measuring the cap instead of quoting it

A CDN's limit need not equal the origin's, and a chain can carry the same 8 KiB twice โ€” nginx's large_client_header_buffers 4 8k default sits on the origin hop, and the CDN in front of it has its own. The lower one decides, and only a request tells you which one it is.

We bisected through the public URL with a padded dummy header on /api/stats, so no 402 was touched and no delivery row was written:

header valuewhole lineresult
8,150 B8,163 B200
8,180 B8,193 B400 โ€” HTML Request Header Or Cookie Too Large

So the cap is 8,190 bytes on the whole line, and the refusal is the edge's: it returns an HTML error page, not a 402, not our JSON error shape. A seller who only logs their own handler errors will never see this failure.

Synthesizing the retry without a key

You do not need to pay anything to measure the return leg. Take one accepted offer from the live 402, echo its extensions byte for byte, add a 65-byte zero signature and a 32-byte nonce, and serialize. That is exactly the shape a real client sends; the signature is invalid, but length is what we are measuring, and length does not care whether the signature verifies. Nothing is submitted and no payment is made.

retry_line = len("PAYMENT-SIGNATURE: ") + len(base64(preflight_payload))
cap        = measured, not assumed
margin     = cap - retry_line

The retry is not uniformly bigger than the 402

The intuition is that a payment payload is heavier than a quote, so the retry must be the larger line. On our own worst route it is the opposite:

route402 valueretry linemargin to 8,190
x402-sol-report5,860 B5,387 B2,803 B
x402-payment-audit5,268 B4,543 B3,647 B
x402-ai-tests3,876 B3,791 B4,399 B
x402-bounties3,996 B3,655 B4,535 B
x402-cron-parse3,752 B3,275 B4,915 B
x402-readability-score3,748 B3,263 B4,927 B
x402-sol-find3,732 B3,255 B4,935 B
x402-base64-decode3,572 B3,091 B5,099 B

The 402 carries fields the retry drops โ€” nextSteps, message, error, budget metadata โ€” and the retry carries the signed payload the 402 does not. The honest relation is arithmetic, not a direction:

retry = 402 โˆ’ (fields only the quote needs) + (signed payload)

A guard that assumed "retry is always larger" would have flagged the wrong route and cleared the real one. Here the worst retry, 5,387 bytes, is 473 bytes smaller than the quote it was built from.

What this means for a seller

One escape hatch, and what it costs

An x402 V2 payment is a signed payload, and its size grows with whatever the seller put in accepts[]. A proof that is already on chain does not grow: the tx-hash flow we document sends X-Payment-Tx (a 66-byte 0x hash) and X-Payer-Signature (a 132-byte signature), a constant ~230 bytes of header no matter how large the quote was.

That is a real fallback for a buyer whose signed payload would cross the cap โ€” at the cost of transferring first and losing the atomicity the signed payload gives you. It is worth knowing the fallback exists before you need it, and worth documenting for your own buyers if you run a seller.

The two failures look identical to the buyer

Both walls produce the same outcome: the call never completes, and no seller-side metric moves. The difference is where the bytes stopped โ€” one in the client's parser, one at the seller's edge. If you run an x402 endpoint and track only handler errors, you are watching neither.

Measurement method: padding bisection through the public URL on a first-party endpoint; retry synthesis from each route's own live 402 with an invalid-but-correctly-sized signature; no keys, no payments, nothing submitted.