Troubleshooting
Ordered by how often they come up.
403 ORIGIN_NOT_ALLOWED
Your key is valid; the origin is not registered.
- Register every origin you serve from — production, staging, preview deploys. Vercel and Netlify preview URLs are distinct origins and each needs registering, or ask us for a wildcard on your preview domain.
- A request with no
Originheader is refused too. That is not a bug: agm_pub_key is bound to an origin, so using it server-side (where browsers do not setOrigin) has nothing to bind. Use agm_live_key on your server. - The refusal is deliberately distinct from
API_KEY_REQUIREDso you can tell "I forgot my key" from "my domain is not registered" — the second is a one-line fix on our side.
403 COLLECTION_NOT_IN_SCOPE
Your key was issued for specific drops and this is not one of them.
The refusal is identical whether or not the collection exists — you learn only that this key cannot read it, never whether the drop is real. So this is also what you get for a typo'd identifier.
An unscoped key reaches nothing at all. A partner key with no collections is a configuration error, not a wildcard.
403 SANDBOX_KEY_ON_MAINNET / LIVE_KEY_ON_TESTNET
Key class and chain must match. A gm_test_ sandbox key works only on test networks; a production key works only on production chains.
The symmetry is deliberate. A live key on devnet is not dangerous, but silently allowing it is how you end up testing with your production credential and believing the sandbox works.
404 COLLECTION_NOT_FOUND on an id you know exists
You are almost certainly passing the internal uuid. That is not a public identifier and is refused on purpose.
Use the short id or the on-chain address.
The price is wrong, or shows 0
You are reading a raw field instead of priceDisplay.
priceDisplayis a union, not a number — see the read model.phase.priceis Dutch-resolved but not peg-resolved, so on a pegged phase it is a stale cached token amount.- Rendering
0as "Free" is the exact bug the resolver exists to prevent. Checkkind === 'amount' && isFreeinstead.
"Mints remaining" is wrong for some wallets
You derived it from the eligibility verdict. walletMints includes bonus mints (BOGO, bounty); the eligibility engine excludes them. Call walletMints and use what it returns.
CLIENT_BROADCAST_NOT_ALLOWED
Your code broadcast the transaction itself, or sent a transactionHash. Sign the bytes prepare returned and send them back as signedTransaction — see Wallets and signing.
TX_MODIFIED
The bytes we were asked to broadcast differ from the bytes we built: different recipient, different amount, or added instructions.
Never retry this
Re-submitting the same bytes cannot succeed and should not. Discard the session and start over with a fresh prepare. If it recurs, something is modifying transactions between us and the wallet, and that is worth telling us about.
SESSION_EXPIRED, or a mint that 409s
prepare sessions are short-lived and single-use. An expired signature needs a fresh prepare; a replayed session loses to an atomic claim and returns 409. Neither is retryable as-is.
err.code is undefined on a failed mint
You minted more than one, which is a batch — every Solana standard mints one NFT per transaction. Batch codes live in results[].errorCode:
const code = err.code ?? err.results?.find((r) => r.errorCode)?.errorCode;429, or mints are throttled
The SDK honours Retry-After and retries automatically. Two sharing details bite in practice:
prepareandexecuteshare one 60/min bucket per key, and a mint needs one of each — so the sustained ceiling is roughly 30 mints per minute per key.- The per-collection prepare cap is shared with gravemint.io. On a hot drop your prepares compete with every first-party collector's, so 60/min is a ceiling you may not reach, not a reservation.
Ask before engineering around a limit.
503 AUTH_UNAVAILABLE
We could not check your key, so we failed closed. This is not a rejection of your credential — retry with backoff.
Nothing appears in your network tab at all
A blocked CORS preflight never leaves the browser, so it looks like an outage rather than a refusal. Check that the origin is registered, and note that a 204 on a preflight is not evidence of success — only an access-control-allow-origin header is.
Still stuck
Tell us the error code, the key prefix (gm_pub_ / gm_live_ / gm_test_, never the key itself), the origin, and the collection identifier. Those four make almost every case answerable immediately.
