| title | Order errors and validation |
|---|---|
| description | Every rejection markets-service can return, and what to change to fix it |
Every error response has the same shape:
{ "error": "action_json.data.isBid must match side" }| Status | Meaning |
|---|---|
400 |
The request is malformed or internally inconsistent. Fix and resubmit. |
403 |
The cancel targets an order you do not own, or a protected namespace. |
404 |
No such order, or the order is no longer active. |
409 |
order_id has already been used. |
500 |
Server-side failure. Safe to retry. |
Most rejections happen before the order reaches the book, so a 400 means nothing was placed.
The digest is over the Action struct exactly as described in
Authentication and signing — domain Matching version 1.0, bound to the chain and
the Matching contract. A mismatch in any signed field produces a different digest and so a
different recovered address. Check that action_json is byte-for-byte what you signed, and that
signer_address is the key that signed it rather than the account that owns it.
Every one of these is required on POST /v1/orders.
| Error | Fix |
|---|---|
order_id is required |
Supply a unique client-side id. |
asset_address is required |
Take it from GET /v1/markets. |
nonce is required |
Supply a nonce unique per (owner, nonce). |
limit_price is required |
Supply the price in human decimals. |
desired_amount is required |
Supply the size in human decimals. |
signature is required |
Supply the EIP-712 signature. |
invalid JSON body |
The body did not parse as JSON. |
action_json must be valid JSON |
action_json is present but not valid JSON. |
These come from decoding action_json.data, the ABI-encoded TradeData tuple.
The flat order fields and the signed action_json describe the same order, so they must agree.
Rebuild both from one set of values rather than editing one of them.
| Error | Compared against |
|---|---|
action_json.subaccount_id must match subaccount_id |
body subaccount_id |
action_json.nonce must match nonce |
body nonce |
action_json.owner must match owner_address |
body owner_address |
action_json.signer must match signer_address |
body signer_address |
action_json.data.asset must match asset_address |
body asset_address |
action_json.data.subId must match sub_id |
body sub_id |
action_json.data.isBid must match side |
true for buy, false for sell |
These are the most common rejections, and they almost always mean the body was sent in wei.
`data.limitPrice` must be a positive whole multiple of the body's `limit_price` after tick normalization. The body takes **human decimals**; only `data` carries wei. Same rule for size, normalized against the instrument's minimum size. `limitPrice` is a signed `int256` but zero and negative values are rejected. Same for `desiredAmount`. `desired_amount` is smaller than the instrument's minimum size, so it normalizes to nothing. `desired_amount` is not a whole multiple of the instrument's minimum size. If a price or amount error appears and the numbers look correct, check the scale first. See [Price and amount use two different scales](/signing#price-and-amount-use-two-different-scales).| Error | Cause |
|---|---|
desired_amount must be greater than zero |
Non-positive size in the body. |
filled_amount cannot be negative |
Negative filled_amount. |
filled_amount cannot exceed desired_amount |
Partial-fill state is inconsistent. |
expiry must be in the future |
expiry is at or before now. Use unix seconds. |
invalid integer value … |
An integer field is not parseable. |
These apply to USDCcNGN-SPOT only. Both fields are optional — omit them and your raw engine
values are used as sent. Send either one and the rules below apply. See the
Spot market guide for the units involved.
Treat order_id as an idempotency key you control. A 409 means the first submission was
accepted, so check its status with GET /v1/orders/{order_id} rather than resubmitting.
| Status | Error | Cause |
|---|---|---|
404 |
active order not found |
Already filled, already cancelled, or never existed. |
403 |
service-tagged cancels are not allowed for protected namespace … |
The order belongs to a protected order_id namespace. |
500 |
failed to resolve cancel target |
Lookup failed server-side. Retry. |
| Endpoint | Error |
|---|---|
GET /v1/trades |
limit must be between 1 and 100 |
GET /v1/trades |
before_trade_id must be a positive integer |
GET /v1/candles |
interval must be one of 1m, 5m, 15m, 1h, 4h, 1d |
GET /v1/candles |
limit must be between 1 and 1000 |
GET /v1/candles |
start must be an RFC3339 timestamp |
GET /v1/candles |
end must be an RFC3339 timestamp |
GET /v1/candles |
end must be after start |
GET /v1/orders/{order_id} |
order not found (404) |
unknown market applies to GET /v1/book, GET /v1/trades, and GET /v1/candles alike.