Skip to content

Discovery probe silently rejects valid V1 body when V2 PAYMENT-REQUIRED header is malformed #804

@dadang11

Description

@dadang11

Summary

In @agentcash/discovery (used by x402scan via apps/scan/src/lib/discovery/probe.ts), the probeMethod function tries parsePaymentRequiredBody2 (V2 header parser) first, but does not fall back to parsePaymentRequiredBody (V1 body parser) when V2 parse returns null. Servers that ship a valid V1 body alongside a malformed V2 payment-required header are silently rejected as "No valid x402 response found".

This is a common transitional pattern in the early x402 ecosystem: many servers emit both formats for client compatibility, but the V2 header construction can be subtly wrong (e.g. base64 of a raw accepts array instead of the proper { x402Version: 2, accepts, extensions } envelope).

Repro

Any server that returns:

HTTP/1.1 402 Payment Required
Content-Type: application/json
payment-required: <base64 of raw accepts array, NOT a V2 envelope>

{"x402Version":1,"accepts":[{...valid V1 fields...}]}

Hits registerFromOrigin and gets "No valid x402 response found" even though the body is V1-valid.

Root cause

packages/.../core/source/probe/index.ts, around probeMethod:

const headerValue = response.headers.get("payment-required");
const parsed = headerValue !== null
  ? parsePaymentRequiredBody2(headerValue)
  : await parsePaymentRequiredBody(response);

When headerValue !== null but parsePaymentRequiredBody2 returns null (e.g. payload is an array, not an object envelope, so isRecord fails), parsed = null and the V1 body is never inspected.

Proposed fix

Always try V1 body when V2 header parse returns null:

const headerValue = response.headers.get("payment-required");
let parsed = headerValue !== null ? parsePaymentRequiredBody2(headerValue) : null;
if (parsed === null) {
  parsed = await parsePaymentRequiredBody(response);
}

Pure additive fallback. V2-only servers unaffected. V1-only servers unaffected. Previously-failing dual-format servers now succeed.

Discovered while

Integrating CryptoIZ MCP (Solana whale intelligence, 9 tools, 7 paid) with x402scan. Our gateway emitted V1 body + V2 header. After diagnosing this bug from the source of @agentcash/discovery@1.6.3, we patched our Cloudflare Worker to drop the malformed V2 header so V1 body fallback kicks in. Result: registration went from 0 registered, 9 failed7 registered, 0 failed.

Reference listing: https://x402scan.com/server/cbd8fff5-d636-4331-b22b-3291717a4e9e

Why fix matters for ecosystem

Many builders shipping x402 MCP servers in early 2026 emit dual format for safety. Without this fallback, x402scan silently misses them. Our team only diagnosed it because we were debugging our own listing — most builders will just see "failed" without knowing why.

Happy to submit a PR if/when the discovery package opens up. For now, flagging as a tracking issue here at x402scan since this is the surface where the symptom appears.

cc @merit_systems team — would love a quick look.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions