15 min
Debugging x402 Clients
Learning Objectives
- Inspect 402 responses and payment requirements
- Identify common error messages
- Use lifecycle hooks for debugging
- Verify wallet configuration
Common Errors
When building x402 clients, you'll encounter these errors:
| Error | Meaning | Solution | |-------|---------|----------| | "No scheme registered for network X" | Client doesn't support the required network | Register the scheme with `registerExactEvmScheme` or `registerExactSvmScheme` | | "Payment already attempted" | Payment was rejected and client won't retry | Check why payment failed, create new request | | "Invalid signature" | Payment signature doesn't match | Check private key format (must have 0x prefix) | | "Insufficient funds" | Wallet has no USDC | Add USDC to wallet | | "Facilitator unreachable" | Can't connect to facilitator | Check network connection, try different facilitator |
Debugging with Hooks
Add verbose logging to see exactly what's happening:
typescript
const client = new x402Client()
.onBeforePaymentCreation(async (ctx) => {
console.log('🔵 [BEFORE] Creating payment');
console.log(' Network:', ctx.selectedRequirements.network);
console.log(' Amount:', ctx.selectedRequirements.amount);
console.log(' PayTo:', ctx.selectedRequirements.payTo);
console.log(' Facilitator:', ctx.selectedRequirements.facilitator);
return undefined;
})
.onAfterPaymentCreation(async (ctx) => {
console.log('✅ [SUCCESS] Payment created');
console.log(' Signature:', ctx.paymentPayload.signature?.slice(0, 20) + '...');
return undefined;
})
.onPaymentCreationFailure(async (ctx) => {
console.error('❌ [FAILURE] Payment creation failed');
console.error(' Error:', ctx.error.message);
console.error(' Stack:', ctx.error.stack);
return undefined;
});Inspecting 402 Responses
When you receive a 402, inspect what the server is asking for:
typescript
// Make request manually to see 402
const response = await fetch('https://api.example.com/protected');
if (response.status === 402) {
const paymentHeader = response.headers.get('PAYMENT-REQUIRED');
const decoded = JSON.parse(atob(paymentHeader));
console.log('Server requires payment:', decoded);
// Shows: network, scheme, amount, payTo, facilitator
// Check if you support this network
console.log('Network:', decoded.accepts[0].network);
// Example: "eip155:8453" (Base)
}If you see "No scheme registered", the network in the 402 response doesn't match what you registered!
Verifying Wallet Setup
Common wallet issues:
typescript
import { privateKeyToAccount } from "viem/accounts";
// ❌ WRONG - No 0x prefix
const key = '1234567890abcdef...';
// ❌ WRONG - Wrong length (must be 64 hex chars + 0x)
const key = '0x1234' as `0x${string}`;
// ✅ CORRECT
const key = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as `0x${string}`;
const signer = privateKeyToAccount(key);
// Verify it works
console.log('Wallet address:', signer.address);
// Should print: 0x...Always verify your signer was created successfully by logging
signer.address. If this throws an error, your private key format is wrong.Knowledge Check
You get "No scheme registered for network eip155:8453". What's wrong?