Common Validation Errors
Common Validation Errors
400 bad_request is the code you'll see most. Most of them are obvious from the details field, but a handful trip up new integrators repeatedly. This page is the cheat sheet.
For the full code catalog, see Error Codes Reference.
The frequent offenders
| Trigger | Where | The rule |
|---|---|---|
Bad name | create-pool, create-token | Must match ^[a-zA-Z0-9 ]{1,20}$ — 1-20 alphanumeric + space chars |
Bad symbol | create-pool, create-token | Must match ^[A-Za-z0-9]{1,10}$ — 1-10 alphanumeric, no spaces |
| Invalid pubkey | Any SolanaAddress field | Must be base58, length 32-44 |
bps out of range | /v2/pools/{address}/fee-claims (kind: "split") | Each entry 1-10000 (integer) |
bps sum wrong | /v2/pools/{address}/fee-claims (kind: "split") | The splits[].bps total must equal exactly 10000 |
splits length | /v2/pools/{address}/fee-claims (kind: "split") | 2 minimum, 10 maximum entries |
entries length | /v2/merkle-trees | 1-50000 entries |
airdrop_count overflow | All airdrop routes | 0-65535 (u16) |
signed_transactions length | /v2/transactions with bundle: true | 1-5 entries |
addresses length | /v2/accounts/batch-fetch | 1-100 entries |
lock_duration_days | /v2/gold-locks | 1-1460 (4-year max) |
| NSFW image | create-token, create-pool | OpenAI moderation flagged the asset |
| Recent-ticker cooldown | create-pool | Same symbol was used within the cooldown window |
mode=signed on user route | Staking, NFT, gold, airdrop claim, token create | Only mode=unsigned — user wallet must sign |
Example 1: bad symbol regex
symbol regexWhat you sent (lowercase and a hyphen — both disallowed):
curl -X POST https://api.piratecrew.fun/v2/tokens \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Black Pearl",
"symbol": "black-pearl",
"payer": "YOUR_WALLET_PUBKEY"
}'{
"error": {
"code": "bad_request",
"message": "Invalid request body",
"param": "symbol",
"doc_url": "https://docs.piratecrew.fun/docs/common-validation-errors"
},
"meta": { "request_id": "req_…" }
}The fix — strip the hyphen, drop to <=10 chars:
-d '{
"name": "Black Pearl",
"symbol": "PEARL",
"payer": "YOUR_WALLET_PUBKEY"
}'Example 2: bps doesn't sum to 10000
bps doesn't sum to 10000A common slip — 50% + 30% + 10% = 90% (missing 1000 bps):
curl -X POST https://api.piratecrew.fun/v2/pools/POOL_ADDRESS/fee-claims \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "split",
"payer": "YOUR_WALLET_PUBKEY",
"splits": [
{ "recipient": "CREATOR_PUBKEY", "bps": 5000 },
{ "recipient": "KOL_PUBKEY", "bps": 3000 },
{ "recipient": "TREASURY_PUBKEY", "bps": 1000 }
],
"mode": "unsigned"
}'{
"error": {
"code": "bad_request",
"message": "splits[].bps must sum to 10000",
"param": "splits"
},
"meta": { "request_id": "req_…" }
}The fix — make the entries total 10000 exactly:
-d '{
"kind": "split",
"payer": "YOUR_WALLET_PUBKEY",
"splits": [
{ "recipient": "CREATOR_PUBKEY", "bps": 5000 },
{ "recipient": "KOL_PUBKEY", "bps": 3000 },
{ "recipient": "TREASURY_PUBKEY", "bps": 2000 }
],
"mode": "unsigned"
}'bps is basis points: 100 bps = 1%, 10000 bps = 100%. If you want a leftover routed to the payer, add it as an explicit splits entry — the API does not auto-fill the remainder.
Example 3: mode=signed on a user-owned endpoint
mode=signed on a user-owned endpointSending mode=signed to a route that requires the user's keypair:
curl -X POST https://api.piratecrew.fun/v2/stakes \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user": "USER_PUBKEY",
"asset": "NFT_ASSET",
"collection": "COLLECTION_ADDRESS",
"mode": "signed"
}'{
"error": {
"code": "bad_request",
"message": "staking must be signed by the user wallet (mode=unsigned only)"
},
"meta": { "request_id": "req_…" }
}These routes return an unsigned VersionedTransaction only — staking, unstaking, NFT mint/burn, gold lock/unlock, airdrop claim, token create, and authority revoke. Switch to mode=unsigned and sign on the client:
-d '{
"user": "USER_PUBKEY",
"asset": "NFT_ASSET",
"collection": "COLLECTION_ADDRESS",
"mode": "unsigned"
}'You'll get back a base64 transaction; sign it with the user's wallet and submit it via POST /v2/transactions. See Transaction Modes for the full matrix of which endpoints support which modes.
Tip: read details first
details firstThe Zod validator dumps the failing path and rule into details. Before you instrument anything fancy, log details — 9 times out of 10 the answer is right there.