hpyhacking/peatio · error · APIv2::OrderNotFoundError
2004
2004
Error message
Order##{id} doesn't exist. What it means
Peatio APIv2 error code 2004 (HTTP 404), raised by GET /api/v2/order. The endpoint runs current_user.orders.where(id: params[:id]).first, so the lookup covers only orders owned by the authenticated member. When no own order carries that id, it raises OrderNotFoundError and returns {error: {code: 2004, message: "Order##{id} doesn't exist."}}. An order that exists under a different member is indistinguishable from one that never existed.
Source
Thrown at app/api/api_v2/orders.rb:32
end
get "/orders" do
orders = current_user.orders
.order(order_param)
.with_currency(current_market)
.with_state(params[:state])
.page(params[:page])
.per(params[:limit])
present orders, with: APIv2::Entities::Order
end
desc 'Get information of specified order.', scopes: %w(history trade)
params do
use :auth, :order_id
end
get "/order" do
order = current_user.orders.where(id: params[:id]).first
raise OrderNotFoundError, params[:id] unless order
present order, with: APIv2::Entities::Order, type: :full
end
desc 'Create multiple sell/buy orders.', scopes: %w(trade)
params do
use :auth, :market
requires :orders, type: Array do
use :order
end
end
post "/orders/multi" do
orders = create_orders params[:orders]
present orders, with: APIv2::Entities::Order
end
desc 'Create a Sell/Buy order.', scopes: %w(trade)
params do
use :auth, :market, :orderView on GitHub (pinned to dab8641137)
Solutions
- List your own orders with GET /api/v2/orders (use state and page filters) and confirm the id appears under the same access key that signs the request.
- Verify the access_key belongs to the member that created the order; switch to the correct key or re-create the order under the current account.
- Keep order ids as strings end to end so they are not float-rounded or truncated before the call.
- Treat code 2004 as terminal: do not retry the same request; drop the stale local reference to that order.
Example fix
// before
const order = await peatio.get('/order', { id }); // 2004 => request fails, bot crashes
// after
const res = await peatio.get('/order', { id }).catch(e => e);
if (res?.error?.code === 2004) {
// not owned by this account or no longer referenceable
forgetOrder(id);
} else {
useOrder(res);
} Defensive patterns
Strategy: validation
Validate before calling
# Ruby client: confirm the id belongs to the signing account before GET /order
mine = client.get '/api/v2/orders', market: 'btcusd', state: 'wait' # own orders only
own_ids = mine.map { |o| o['id'] }
raise "id #{id} not owned by this account" unless own_ids.include?(id)
order = client.get '/api/v2/order', id: id Type guard
// TypeScript: order ids are opaque positive integers — narrow before use const isOrderId = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v) && v > 0;
Try / catch
Catch the HTTP 404 whose body.error.code === 2004 and treat it as terminal: remove the order from local state and continue; never retry the same id unchanged.
Prevention
- Keep order ids as strings end to end to avoid 2^53 precision loss in JavaScript.
- Use one access key per account and assert the key's member matches the order owner before querying.
- Cache order state from GET /api/v2/orders and drop entries you no longer see instead of probing them one by one.
- Scope check: the token needs 'history' or 'trade' — a missing scope surfaces as 2011, not 2004, so verify scopes separately.
When it happens
Trigger: GET /api/v2/order signed with account A's access key while the id was created by account B; a typo'd, truncated, or zero id; an id copied from another environment (staging vs production) or from data predating a database restore. The token must carry the history or trade scope, and the id must be an integer (params :order_id requires type Integer).
Common situations: A bot replays cached order ids from a testnet deployment against production; two API keys for two accounts and the client signs with the wrong one; ids serialized through JavaScript lose precision above 2^53; long-gone orders still referenced by a local cache after a DB purge.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of hpyhacking/peatio@dab8641137 (2026-08-23).
Data as JSON: /api/errors/956ddf3f64fdb383.
Report an issue: GitHub.