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

TriggerWhereThe rule
Bad namecreate-pool, create-tokenMust match ^[a-zA-Z0-9 ]{1,20}$ — 1-20 alphanumeric + space chars
Bad symbolcreate-pool, create-tokenMust match ^[A-Za-z0-9]{1,10}$ — 1-10 alphanumeric, no spaces
Invalid pubkeyAny SolanaAddress fieldMust 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-trees1-50000 entries
airdrop_count overflowAll airdrop routes0-65535 (u16)
signed_transactions length/v2/transactions with bundle: true1-5 entries
addresses length/v2/accounts/batch-fetch1-100 entries
lock_duration_days/v2/gold-locks1-1460 (4-year max)
NSFW imagecreate-token, create-poolOpenAI moderation flagged the asset
Recent-ticker cooldowncreate-poolSame symbol was used within the cooldown window
mode=signed on user routeStaking, NFT, gold, airdrop claim, token createOnly mode=unsigned — user wallet must sign

Example 1: bad symbol regex

What 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

A 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

Sending 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

The 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.