Skip to content

Errors

When a request fails, the body is an error envelope:

{
"error": {
"code": "NOT_FOUND",
"message": "Not found: VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f",
"retryable": false,
"requestId": "0f8c…"
}
}
  • retryable — whether retrying the same request can succeed. Retryable errors also include retryAfterMs and a Retry-After response header (in seconds).
  • action — sometimes present: a human-readable next step (e.g. on PLAN_REQUIRED).
  • requestId — also in the X-Request-Id header. Quote it in support requests.
codeHTTPMeaningRetry-After
UNAUTHORIZED401Missing, invalid, or revoked token
PLAN_REQUIRED403The shop isn’t on the Enhanced plan (the REST API is Enhanced-only)
FORBIDDEN_SCOPE403A PATCH with a read-only key
INVALID_INPUT400A bad query parameter or body (the message names the problem), or a write against a Shopify-managed material
NOT_FOUND404Unknown path, or the id doesn’t exist
RATE_LIMITED429Per-shop rate limit reached
DO_OVERLOADED503The shop’s data store is busy
DO_UNAVAILABLE503The data store is temporarily unavailable
UPSTREAM_SHOPIFY502A Shopify-facing dependency failed (e.g. the live inventory read)if present
TIMEOUT504The request timed outif present
OUTPUT_TOO_LARGE413The response exceeded the size cap
INTERNAL500An unexpected error

An unsupported method returns 405 with code METHOD_NOT_ALLOWED and an Allow header — the only code outside the table above.

A PATCH whose path id is not a VMAT-* id fails fast with 400 INVALID_INPUT:

{
"error": {
"code": "INVALID_INPUT",
"message": "Shopify-managed materials are read-only in API v1 — only virtual (VMAT-*) materials can be modified (got '45067340286136')",
"retryable": false,
"requestId": ""
}
}

Changing the unit of a material that is already used in BOMs or sub-assemblies is only allowed when a usable unit conversion exists. When it doesn’t, the app’s conflict response is passed through as 400 INVALID_INPUT — the message carries the app’s explanation of the conflict (which units were involved and why no conversion applies), so you can surface it directly to the user.

On 429 / 503, wait Retry-After seconds (or retryAfterMs) and retry the same request. Don’t fan out parallel requests against one shop — the data store is single-threaded per shop, so concurrency doesn’t go faster and can trip the rate limit.