Metadata validation
Symptom
{
"valid": false,
"errors": [
{ "path": "/productV1/title", "message": "required" },
{ "path": "/productV1/images/0/url", "message": "must match format" }
]
}Or the on-chain createOffer reverts with an opaque "metadata hash mismatch" or similar.
Causes
1. Schema mismatch
You're using PRODUCT_V1 fields with a BASE schema (or vice versa). Check type on the top-level metadata object.
2. Required field missing
The error path tells you where. Look at the corresponding zod schema in @bosonprotocol/metadata — every field is required unless explicitly optional.
3. URL format issues
Image / file URLs must be:
- HTTPS (
https://...), or - IPFS (
ipfs://Qm...).
http:// is rejected. Some clients also reject data URLs and relative paths.
4. UUID format
uuid must be a v4 UUID string (the crypto.randomUUID() output). Random strings won't pass.
5. Length overflow
Metadata is capped at 20 MB. If you embed images in dataUrl form, you'll blow this. Use CDN URLs or IPFS references for images.
6. Metadata-hash mismatch on-chain
If validate_metadata passes but createOffer reverts, your metadataHash doesn't match what metadataUri resolves to. The simplest fix is to derive the hash from the URI you got back from storeMetadata — the IPFS CID is the hash:
const metadataUri = await sdk.storeMetadata(metadata)
const metadataHash = metadataUri.replace(/^ipfs:\/\//, "")
// pass both into createOfferDebugging
Always run validate_metadata before storeMetadata. Iterate until clean, then pin. This saves wasted IPFS writes.
const { valid, errors } = await sdk.validateMetadata(metadata)
if (!valid) { console.error(errors); process.exit(1) }
const uri = await sdk.storeMetadata(metadata)