Skip to content
OmniRetailConnect Docs

Commerce · Getting started

Errors and status codes

HTTP status codes, REST error codes, MCP transport errors and tool errors.

View as Markdown

Overview

Both interfaces fail in two layers: a plain HTTP response when the request never reaches a capability (authentication, rate limit, size, routing), and a capability error when it does and fails (validation, scope, not found). Responses to routed requests of the MCP server and of the REST API carry Cache-Control: no-store (no-store, private on the API). Responses the framework answers itself, such as the 404 or 405 of a switched-off feature, or OPTIONS, PUT and PATCH on the MCP URL, carry no-cache, private instead.

REST API

Every REST error is JSON with an error object. Messages are in English and safe to show; branch on code, not on the message. Errors never contain stack traces, SQL or paths.

Error body
{
  "error": {
    "code": "invalid_argument",
    "message": "Unknown argument(s): foo."
  }
}
REST API errors
Status error.code When
401 unauthenticated Unauthorized. Missing, malformed, unknown, revoked or expired token, or a token without the api channel. Identical for every cause. Has WWW-Authenticate: Bearer.
403 forbidden_scope The token lacks the scope for the endpoint or option, for example Searching orders by email requires the commerce.read.pii scope.
403 https_required HTTPS is required. Production only: the request is not secure. If you already use https://, ask the shop operator to check the reverse-proxy configuration.
404 not_found The resource does not exist, the path is unknown (after authentication), a path value is malformed (/products/abc, a product id that is not digits only, or invalid percent-encoding or UTF-8 such as /products/%FF, for an authenticated caller), a parent category does not exist, or the API is switched off.
405 method_not_allowed A method the existing path does not support, after authentication. Allow lists the methods of the path: GET, HEAD for reads, plus POST, PUT, PATCH or DELETE on the paths that have a write endpoint, for example GET, HEAD, PUT, DELETE on /skus/{sku}/sale.
409 conflict A write conflicts with the current state: stock cannot be set while an external inventory provider manages it; an order is not an unpaid draft (confirm, cancel), a shipment is not allowed or an idempotency_key was used with other data; a discount code is taken or already used (delete); a page was saved after your expected_updated_at; or the data changed during the call or stayed locked by another change.
414 uri_too_long The URL (path plus query string) is longer than the URL cap, 4096 bytes by default.
422 invalid_argument Failed validation: for example Unknown argument(s): foo., a bad or foreign cursor, conflicting or missing parameters, a range that is too long, or a parameter name or value that is not valid UTF-8. On a write also a body that is not a JSON object, not application/json, larger than 65536 bytes or that repeats a URL parameter.
422 field_owned_by_pim A write touches a field that the PIM owns on a product linked to the PIM; the message names the field.
429 too_many_requests Too many requests. A rate limit was exceeded. Has Retry-After (seconds).
500 internal_error An internal error occurred. Unexpected failure. Details are logged on the server without personal data and never sent to the client. Safe to retry later. An authenticated caller also gets this for HTTP errors other than 404, 405, 414 and 429, for example 503 during maintenance.

The 401 response

Everything under /api/v1 except openapi.json is behind authentication, including paths that do not exist and methods the path does not support. An unauthenticated caller therefore gets this identical response for every request: any HTTP method (including TRACE, PURGE or PROPFIND), any path, the bare prefix /api/v1, and paths with a malformed percent-encoding or invalid UTF-8 such as /api/v1/products/%FF. It learns nothing about which paths exist. Only GET /api/v1/openapi.json is public. Only an authenticated caller is told 404 (no such path, the bare prefix or a malformed path) or 405 (path exists, wrong method; Allow lists the methods of that path).

401 · with WWW-Authenticate: Bearer
{
  "error": {
    "code": "unauthenticated",
    "message": "Unauthorized."
  }
}

Order of checks

API enabled, HTTPS, URL length, per-IP limit, authentication, per-token limit, then the endpoint (scope, validation, lookup). Callers without a valid token therefore meet the 401 after the cheap edge checks, which do not depend on the path (403 https_required, 414) and the per-IP limit. Because the per-IP limit comes first, a flood of bad tokens from one address can also throttle a valid token from that same address (429). Other rules: the token of a request is refused from the exact instant it expires, and with several Bearer values in one Authorization header the last one is used (send exactly one).

MCP: transport level

These are plain HTTP responses, in addition to normal JSON-RPC answers.

MCP HTTP status codes
Status Body When
400 JSON-RPC error Unparsable JSON (-32700); a body that is not a JSON-RPC object (-32600); invalid params for tools/list or tools/call (-32602).
401 {"message":"Unauthorized."} Missing, malformed, unknown, revoked or expired token, or a token without the mcp channel. All cases look identical. The response has the header WWW-Authenticate: Bearer realm="mcp", error="invalid_token".
403 JSON-RPC error -32600 The request is not secure (production only): HTTPS is required. Use an https:// URL. If you already use https, ask the shop operator to check the reverse-proxy configuration.
404 JSON-RPC error -32601 The JSON-RPC method is unknown (with a valid token). A switched-off server or a wrong path is not answered by the MCP endpoint: see "Switched-off features" below. The body {"message":"Not Found."} only appears when the server was switched off after the route list was cached.
405 — GET or DELETE on the endpoint, with a valid token. The response has Allow: POST. Use POST. Without a valid token these requests get the 401 first.
413 JSON-RPC error -32600 The request body is larger than 256 KiB (64 KiB before server version 1.6.0).
429 {"message":"Too Many Attempts."} A per-IP or per-token rate limit was exceeded. The response has Retry-After (seconds). Branch on the status code and Retry-After, not on the message text.

The checks run in this order: server enabled, HTTPS, body size, per-IP limit, authentication, per-token limit, body shape. A tools/call for an unknown tool name answers a JSON-RPC -32602 error.

See Rate limits, pagination and request limits for the numbers behind 413, 414 and 429.

Switched-off features

While a feature is switched off, GET answers the shop's normal 404 and POST, PUT, PATCH and DELETE answer 405 (Allow: GET, HEAD), as for any unknown path. It is the shop's own 404 page, with a JSON body {"message":""} for API clients and an HTML page for browsers. A POST that answers 405 with Allow: GET, HEAD (message "The POST method is not supported for route mcp/commerce. Supported methods: GET, HEAD.") therefore means the URL is wrong or the MCP server is not enabled for this shop. The REST API behaves the same way. Only when the route list was cached while a feature was enabled and it was switched off later, the REST API answers the envelope {"error":{"code":"not_found","message":"Not found."}} with 404 for every method, and the MCP endpoint answers {"message":"Not Found."} with 404 for GET, POST and DELETE.

MCP: tool errors

A failed tool call is still a normal tools/call result, with isError: true. The text content is JSON with a code and a message that is safe to show to the client.

Text content of a failed tool call
{
  "code": "invalid_argument",
  "message": "The cursor is invalid. Start again without a cursor."
}
Tool error codes
Code Meaning
invalid_argument Failed validation: an unknown argument, a bad cursor, conflicting or missing identifiers, or a range that is too long. REST: 422.
not_found The product, SKU, image, order, category or parent category does not exist. REST: 404.
forbidden_scope The token lacks the scope for the tool or option, for example an order search by email without commerce.read.pii. REST: 403. Write tools are not shown to a token without the write scope; calling one answers JSON-RPC -32602, as for an unknown tool.
field_owned_by_pim Write tools: the field, image or category placement belongs to the PIM on a product linked to the PIM, so the next import would overwrite it. Change it in the PIM. REST: 422.
conflict Write tools: set_stock while an external inventory provider manages stock, create_product in a shop that gets its products from the PIM, or the data changed during the call or stayed locked by another change. Read again and retry. REST: 409.
too_many_requests Write tools: the token made more than 30 write calls this minute (Too many write requests. Try again in a minute.). The call did not run; there is no Retry-After. Wait a minute. REST: 429.
internal_error An unexpected failure. The message is always An internal error occurred. Details are logged on the server without personal data. REST: 500.

The write tools and their errors are described in the tool reference.

Unknown SKUs are not errors

check_sku_availability and GET /availability report an unknown SKU as found: false, and a variant whose inventory source cannot be reached as status unavailable. Neither is an error.