Error codes and fixes
HTTP API errors: non-2xx with {"error":{"code","message","fix?"}}. Contract calls revert with a named error; the relay
(POST /v1/relay) refuses a call that would revert with relay_reverted and the name in message, and the paywall and
the sign page decode them the same way. Apply the fix and retry once; never retry in a loop.
HTTP API (code uses underscores)
| code | status | meaning / fix |
|---|---|---|
bad_request, bad_json, bad_address, bad_amount, bad_message | 400 | malformed input; fix the body/query |
bad_domain, bad_nonce, bad_signature, expired, wrong_chain | 400/401 | SIWE login problems: request a fresh nonce and sign the new message |
unauthorized, bad_token | 401 | missing/expired bearer token: log in again |
forbidden, no_access, no_license, not_operator, no_profile | 403/409 | caller lacks rights: use the studio owner/operator key |
read_the_storefront | 409 | licence attestation asked for a storefront game: read the developer's storefront on chain instead (https://cassette.gg/agents/verify-ownership.md) |
refunded | 403 | legacy Checkout games only (Cassette's own refund record). Storefront refunds burn the tokens instead |
hosting_off / hosting_first_party_only | 410/403 | Cassette doesn't host builds: host in your own account (https://cassette.gg/agents/host-game.md), then register the pointer (https://cassette.gg/agents/publish-game.md) |
personal_data_in_reason | 422 | a public takedown reason contains an email, phone number, @username or wallet address: describe the broken rule instead |
target_not_allowed, function_not_allowed, value_not_allowed, gas_too_high, deadline_too_far, bad_request_signature | 400/403 | relay policy: only platform contracts or factory storefronts, no token transfers or approvals, value 0, gas ≤ 1.5 M, deadline ≤ 24 h |
relay_reverted | 422 | the call would revert; message names the reason |
relay_unavailable | 503 | relayer, or the storefront router, not deployed: retry later |
not_found, not_ready, not_playable, delisted | 404 | unknown / still indexing (retry in seconds) / not playable |
already_claimed, already_released | 409 | community listing state |
invalid_manifest, missing_manifest, bad_zip, unsafe_path, too_many_files, too_large, data_too_large | 413/422 | legacy build upload problems |
rate_limited | 429 | wait and retry (relay: 20 tx/min and 500/day per wallet) |
faucet_unavailable, not_deployed | 503 | platform not fully up: retry later |
Contract reverts (storefront, factory, router)
Decode a revert's first 4 bytes with cast 4byte-decode <data> or your library's ABI decoder
(https://cassette.gg/agents/paywall.md lists the storefront's errors).
| error | meaning | fix |
|---|---|---|
NotStorefront | That address is not a storefront created by the Cassette storefront factory. | Check the storefront address in cassette.json (factory.isStorefront must be true). |
ERC2771UntrustfulTarget | That address is not a storefront created by the Cassette storefront factory. | Check the storefront address in cassette.json (factory.isStorefront must be true). |
NotOwner | Only the storefront's owner can do that. | The human (the owner wallet) signs it: send them a sign link (protocol.md §6). |
NotOperator | This key can't manage the storefront's listings. | Send the human a set-operator sign link for this key, or put the call itself in a sign link (protocol.md §6). |
ListingInactive | It is not on sale right now. | Turn it back on with updateListing(…, active true, …) (https://cassette.gg/agents/sell-items.md). |
NoSuchListing | That item or licence is not listed. | Create the listing (createListing, https://cassette.gg/agents/sell-items.md). |
AlreadyLicensed | This wallet already owns the game. | Nothing to buy: refresh ownership. |
FreeClaimUsed | This wallet already claimed it. | Nothing to claim: refresh ownership. |
PriceOverMax | The price went up since it was shown. Nothing was charged. | Show the new price and ask again. |
PermitAmountMismatch | The payment authorisation didn't match the price. Nothing was charged. | Sign a permit for exactly the total (quote). |
BadQuantity | That quantity is not allowed. | Licences and free listings are 1 per wallet; quantities must be at least 1. |
SelfRefundUnavailable | This purchase can't be refunded from here. | Check canSelfRefund(saleId) for the reason (window closed, gift, cap, seller decision, registry); the seller can always refund. |
NotReleasable | That sale is still inside its refund window, or already paid out. | Release after the window ends (release([saleId]), https://cassette.gg/agents/handle-refunds.md). |
AlreadyRefunded | That sale was already refunded. | Nothing to do. |
BadRefundPolicy | Refund window over 14 days, or a registry other than Cassette’s. | Use a window of 0–336 hours and the factory abuse registry or none. |
BadReferral | Referral fee over 30%, or a fee without a partner. | Keep the referral as the factory proposes it. |
BadText | A name or URL is too long or has characters a storefront rejects. | URLs: visible ASCII, at most 512 bytes. |
BadListing | The listing is out of bounds (price, supply, or a consumable licence). | Prices < 2^128, supply ≤ 2^53−1, only items may be consumable. |
TermsChanged | The seller changed the terms, the seller details or the refund rules since they were shown. Nothing was charged. | Show the current terms again (refresh) and ask again. |
NoSellerRecord | The storefront needs its seller record (URI and hash) first. | Publish the seller record: setSellerRecord(uri, hash) (https://cassette.gg/agents/set-up-seller.md). |
ERC2771ForwarderExpiredRequest | The signed request expired before it was sent. | Re-run the command (it signs a fresh request). |
ERC2771ForwarderInvalidSigner | The signed request doesn't match its sender. | Re-run the command with the same key. |
InvalidAccountNonce | The signed request was already used or out of order. | Re-run the command (it reads the current nonce). |
SupplyExhausted | Sold out. | List a new item (supply is fixed per listing). |
TokensGone | What was bought was transferred or used, so it cannot be returned. | The seller can still refund it. |
SkuInUse | That sku is already listed in this storefront. | Update the existing listing (updateListing) or pick another sku. |
Paywall and play-frame results
StorefrontTxResult.reason and TopUpResult.reason from the play-frame bridge (https://cassette.gg/agents/paywall.md):
cancelled (the player closed the wallet; nothing charged), signed-out, wrong-chain, not-a-storefront (the
address isn't a factory storefront: payment stopped), unsupported (this host can't do it: gifts, or a top-up where
none is offered), insufficient-funds (say how much USDC to add; the host's top-up when it offers one), failed (with error.name when the chain refused).
Hosting
Hosting tool failures (login, limits, names taken), Arweave funding (host-fund, once Arweave opens) and games a
host can't serve (arweave-unsuitable): https://cassette.gg/agents/host-game.md.