Skip to main content
The Store API uses a consistent, Stripe-style error format across all endpoints. Every error response includes a machine-readable code and a human-readable message.

Error Response Format

All errors return a JSON object with a single error key:
{
  "error": {
    "code": "record_not_found",
    "message": "Product not found"
  }
}
Validation errors include an additional details field with per-field error messages:
{
  "error": {
    "code": "validation_error",
    "message": "Name can't be blank and Email is invalid",
    "details": {
      "name": ["can't be blank"],
      "email": ["is invalid"]
    }
  }
}

Schema

FieldTypeDescription
error.codestringMachine-readable error code (see table below)
error.messagestringHuman-readable description of the error
error.detailsobjectField-specific validation errors. Each key is a field name, each value is an array of error strings. Only present for validation errors.

HTTP Status Codes

StatusMeaningWhen
400Bad RequestMissing required parameters, malformed JSON, invalid arguments
401UnauthorizedMissing or invalid API key, expired JWT token
403ForbiddenAuthenticated but not authorized for this resource
404Not FoundResource doesn’t exist or isn’t accessible
409ConflictResource was modified by another request (concurrent update)
422Unprocessable ContentValidation failed, invalid state transition, payment error
429Too Many RequestsRate limit exceeded (see Rate Limiting)

Error Codes

Authentication & Authorization

CodeStatusDescription
authentication_required401Request requires a valid JWT token
authentication_failed401Email or password is incorrect
invalid_token401API key or JWT token is invalid or expired
invalid_provider400OAuth provider not recognized
access_denied403User doesn’t have permission for this action

Resource Errors

CodeStatusDescription
record_not_found404Resource doesn’t exist or isn’t accessible in the current store
resource_invalid422Resource couldn’t be saved

Validation Errors

CodeStatusDescription
validation_error422Model validation failed. Check details for field-specific messages.
parameter_missing400A required parameter is missing
parameter_invalid400A parameter has an invalid value

Cart Errors

CodeStatusDescription
cart_not_found404Cart doesn’t exist or doesn’t belong to the current user/guest
cart_cannot_complete422Cart cannot be completed (e.g., missing payment or address)
cart_cannot_transition422Internal state machine transition failed (e.g., completing a cart that isn’t ready)
cart_empty422Cart has no line items
cart_invalid_state422Cart is in an invalid state for this operation
cart_already_updated409Cart was modified by another request (optimistic locking conflict)

Order Errors

CodeStatusDescription
order_not_found404Completed order doesn’t exist or doesn’t belong to the current user/guest

Line Item & Stock Errors

CodeStatusDescription
line_item_not_found404Line item doesn’t exist in this order
variant_not_found404Variant doesn’t exist or isn’t available
insufficient_stock422Not enough stock to fulfill the requested quantity
invalid_quantity422Quantity must be a positive integer

Payment Errors

CodeStatusDescription
payment_failed422Payment was declined or couldn’t be processed
payment_processing_error422Error communicating with the payment gateway
gateway_error422Payment gateway returned an error

Digital Download Errors

CodeStatusDescription
attachment_missing403File attachment not found for this digital product
download_unauthorized403User is not authorized to download this file
digital_link_expired403Download link has expired
download_limit_exceeded403Maximum number of downloads reached

Concurrency & Idempotency Errors

CodeStatusDescription
order_already_updated409Order was modified by another concurrent request. Retry with fresh data.
idempotency_key_reused422Idempotency key was already used with different request parameters. See Idempotency.

Other Errors

CodeStatusDescription
rate_limit_exceeded429Too many requests. Retry after the Retry-After header value.
request_too_large413Request body exceeds the size limit
processing_error422Generic server-side processing error
invalid_request400Request is malformed (e.g., invalid JSON body)

Examples

Not Found

curl https://api.mystore.com/api/v3/store/products/prod_nonexistent \
  -H "Authorization: Bearer spree_pk_xxx"
404
{
  "error": {
    "code": "record_not_found",
    "message": "Product not found"
  }
}

Validation Error

curl -X POST https://api.mystore.com/api/v3/store/account/addresses \
  -H "Authorization: Bearer <jwt_token>" \
  -H "Content-Type: application/json" \
  -d '{}'
422
{
  "error": {
    "code": "validation_error",
    "message": "First name can't be blank, Address can't be blank, City can't be blank, and Country can't be blank",
    "details": {
      "firstname": ["can't be blank"],
      "address1": ["can't be blank"],
      "city": ["can't be blank"],
      "country": ["can't be blank"]
    }
  }
}

Insufficient Stock

curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/items \
  -H "Authorization: Bearer spree_pk_xxx" \
  -H "X-Spree-Token: abc123" \
  -H "Content-Type: application/json" \
  -d '{"variant_id": "var_xxx", "quantity": 999}'
422
{
  "error": {
    "code": "insufficient_stock",
    "message": "Quantity selected exceeds available stock"
  }
}

Invalid State Transition

curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/complete \
  -H "Authorization: Bearer spree_pk_xxx"
422
{
  "error": {
    "code": "cart_cannot_transition",
    "message": "Cannot transition cart to complete — ensure address, shipping, and payment are set"
  }
}

Handling Errors in the SDK

The @spree/sdk package throws a SpreeError for all non-2xx responses:
import { SpreeError } from '@spree/sdk';

try {
  const product = await client.products.get('nonexistent');
} catch (error) {
  if (error instanceof SpreeError) {
    console.log(error.code);    // 'record_not_found'
    console.log(error.message); // 'Product not found'
    console.log(error.status);  // 404
    console.log(error.details); // undefined (or field errors for validation)
  }
}

Common Patterns

Handle specific error codes:
try {
  await client.carts.items.create(cartId, {
    variant_id: variantId,
    quantity: 1,
  });
} catch (error) {
  if (error instanceof SpreeError) {
    switch (error.code) {
      case 'insufficient_stock':
        showNotification('This item is out of stock');
        break;
      case 'variant_not_found':
        showNotification('This product is no longer available');
        break;
      default:
        showNotification(error.message);
    }
  }
}
Display validation errors per field:
try {
  await client.customer.addresses.create(addressData);
} catch (error) {
  if (error instanceof SpreeError && error.details) {
    // error.details = { city: ["can't be blank"], zipcode: ["is invalid"] }
    for (const [field, messages] of Object.entries(error.details)) {
      setFieldError(field, messages.join(', '));
    }
  }
}