Debug by symptom
x402 problems are easy to fix once you stop guessing and read what the failure is telling you. The protocol is small, the failures are specific, and the wire helpers and adapter name their causes precisely, so debugging is mostly matching a symptom to its cause rather than spelunking. This page is organized by the symptom you see, with the cause and the fix for each.
It assumes the x402 mechanics from how-to-implement-x402-protocol and the adapter setup from how-to-add-x402-to-my-api. The payment API product page is the broader reference. Start with the symptom that matches yours.
The agent keeps getting a 402
Symptom: your agent calls a paid endpoint and a 402 comes back to your code instead of a result. Cause: the pay side is not answering the 402. A plain fetch does not pay; it just returns the 402 as a response.
The fix is to call through the x402 client, which does the settle-and-retry for you:
import { createX402Client } from "@blockchain0x/x402/client";
const fetchWithPay = createX402Client({ sdk });
const res = await fetchWithPay(url); // answers the 402 internally; you get the final responseIf you already use fetchWithPay and still see a 402, you are likely inspecting the response too early or adding your own 402 branch on top. Await the final response from the client and do not double-handle the 402; the client owns that step.
Verification keeps failing
Symptom: on the receive side, genuine payments or webhooks fail verification. The overwhelmingly common cause is parsing the body before verifying. The signature is an HMAC over the exact bytes that arrived, so any re-serialization breaks it.
The fix is to verify against the raw bytes. For webhooks, mount the raw-body parser and pass the Buffer in:
app.use(express.raw({ type: "application/json" }));
// ... webhooks.verify({ headers, rawBody: req.body, secret }); then JSON.parse afterwardsIf the raw body is correct and verification still fails, check the clock: the verifier rejects deliveries more than five minutes from its own time, so a badly skewed server clock looks like a signature failure. Sync the clock or, if skew is genuinely unavoidable, use the documented tolerance override.
Wrong network or amount
Symptom: the payment is rejected as not matching, or it settles in development and fails in production. Cause: a network or amount mismatch. A sk_test_ agent quotes and settles on Base Sepolia; a sk_live_ one on Base mainnet. A payer on one network cannot satisfy a requirement on the other, and the server rejects a mismatched key with apikey.network_mismatch.
The fix is to confirm both ends agree. Check that the payer and the priced endpoint are on the same network, that the right key prefix is loaded in each environment, and that the amount the payer sends matches the quoted amountWeiUsdc in 6-decimal base units. A surprising amount of "it worked yesterday" turns out to be a test key loaded where a live key belonged.
A 503 on settle
Symptom: the settle path returns a 503. Cause: outbound settlement is in active rollout, and on a network whose chain adapter is not yet live the settle can return a transient 503. This is not a bug in your code.
The fix is to treat it as transient: retry with bounded backoff (covered in how-to-handle-failed-agent-payments), and surface it honestly rather than as a refusal. If a 503 persists on a network you expected to be live, that is the signal to raise it as an environment issue rather than keep changing your code, which is otherwise correct.
Read the error codes
The fastest debugging tool is the code the failure already gives you, so log it everywhere. Parsing a 402 can throw an X402WireError with response.not_402 (the response was not a 402), response.body_missing, or response.body_malformed (not the version-1 shape) - these mean the upstream is not speaking x402 as you expect. Parsing the payment header can throw header.missing, header.malformed, header.unknown_scheme (a scheme other than exact-usdc), or header.payload_malformed. The adapter's verification additionally reports requirement_mismatch (amount or network did not match the quote) and settle_rejected (the chain refused the settle).
Each code points at one cause, so log it on every rejection and let it route you: a header.unknown_scheme is a client sending the wrong scheme, a requirement_mismatch is a price or network disagreement, a settle_rejected is a chain-side problem. The codes turn "the payment failed" into "this exact thing failed", which is the whole game in debugging.
A quick triage flow
When you are not sure which symptom you have, a short triage gets you there fast. Ask, in order:
Did the 402 reach your agent code? If yes, the pay side is not wrapping the client; that is the unanswered-402 case. If no, the call either succeeded or failed after payment, so keep going. Did verification fail on the receive side? If yes, suspect the raw-body issue first, then the clock; read the X402WireError code to confirm which. Did the adapter report requirement_mismatch or apikey.network_mismatch? Then it is a network or amount disagreement; align the two ends. Did you get a 503? Transient settle, back off. None of those? Then it is likely an ordinary upstream error after a successful payment, which you debug like any API failure.
That sequence resolves nearly every x402 issue in a couple of minutes, because each branch lands on a named cause with a known fix. The mistake is starting from "the payment is broken" as one undifferentiated problem; start from where in the flow it broke instead.
Log for future debugging
The reason x402 is fast to debug is that the failures carry causes, but only if you logged them. Make that automatic. On the pay side, log the status and any X402WireError code from a failed call. On the receive side, log the adapter's rejection reason and the relevant identifiers (the payment request, the network, the amount) on every non-2xx. For webhooks, log the verify result code and the delivery id.
Done once, this turns the next incident from an investigation into a lookup: you open the log, read the code, and apply the matching fix from the sections above. The teams that find x402 hard to debug are usually the ones that logged "payment failed" and nothing more; the teams that find it easy logged the code. Spend the few minutes to log the causes now, and every future payment problem is a faster fix.
Common pitfalls
Three debugging traps.
Guessing instead of reading the code. The X402WireError code or adapter reason names the cause. Log it first; it usually tells you the fix.
Re-serializing the body before verifying. The single most common verify failure. Verify the raw bytes.
Treating a 503 as a code bug. It is a transient rollout condition on some networks. Retry and surface honestly before you go hunting in your own code.
What to ship today
When an x402 payment misbehaves, find the symptom above, read the X402WireError code or adapter reason it produced, and apply the matching fix: wrap the client for an unanswered 402, verify raw bytes for signature failures, align networks for mismatches, back off on a 503. Log the code on every failure so the next one is faster. For the protocol internals see how-to-implement-x402-protocol. Pricing is on the pricing page.