Handle errors in Cart management

Learn how to properly manage errors when implementing Cart actions using the Composable Commerce SDK.

After completing this page, you should be able to:

  • Implement robust error handling to manage common API responses, including a recovery strategy for 409 Conflict errors.
  • Apply a backend security check to validate that a user is only able to access and modify their own Cart.

When working with Carts in Composable Commerce, several types of errors can occur. Understanding and handling errors correctly ensures a smoother shopping experience for your customers.

Typical error scenarios include:

  • Version conflicts: another process updated the Cart between your read and update actions.
  • Invalid data: incorrect SKU, currency, or other required fields missing.
  • Cart not found: attempting to operate on a Cart that no longer exists or was deleted.
  • Authorization errors: using an incorrect anonymousId, customerId, or missing permissions.
  • Price mismatch: price selection failed because no matching price was found for country, currency, and Channel.
  • CartState issues: trying to update a Merged, Frozen, or Ordered Cart.

Error responses have the following structure across the different Composable Commerce APIs:

{
  "statusCode": 401,
  "message": "Example error message.",
  "errors": [
    {
      "code": "invalid_token",
      "message": "Example error message."
    },
  ]
}
  • statusCode: HTTP status code corresponding to the error.
  • message: first error message in the errors array.
  • errors: errors returned for a request. A single error response can contain multiple errors if the errors are related to the same HTTP status code such as 400.

Below, is a breakdown of common errors, their details, and suggestions on how to handle them:

Status codeMeaningSuggested handling
400 Bad RequestIndicates an issue with the request itself, such as invalid JSON or missing required fields.Consult the docs and validate input before sending.
401 UnauthorizedOccurs when your API client lacks the necessary authentication credentials or scopes.Revise scopes of the API key, ensure you have a valid access token and the required scopes for the operation.
403 ForbiddenYour API client has valid credentials but lacks permission to perform the requested action.Verify your API client’s roles and permissions.
404 Not FoundThe requested Cart or resource does not exist.Make sure you are using the right Cart ID or key.
429 Too Many RequestsYou’ve exceeded the API rate limit.Implement rate limiting and retry mechanisms in your application.
5xx Server ErrorsIndicate an issue on the Composable Commerce side.Retry your request after a short delay. If the issue persists, contact Composable Commerce support.
You can find more details about these errors in the Composable Commerce API error documentation.

Best practices for error handling

Composable Commerce uses standard HTTP status codes to indicate errors. Your error-handling logic should:

  • Catch errors in API requests.
  • Examine the statusCode and errors array in the API response.
  • Implement retry mechanisms for recoverable errors like server errors or rate limiting.
  • Log all errors for debugging and monitoring.
  • Handle errors gracefully. Provide informative error messages to the customer and avoid exposing technical details.

Handle version conflicts

Version conflicts occur when you try to update a Cart that has already been modified.

Here’s how Zen Electron handles version conflict when executing a CartUpdateAction such as adding or removing an item from the Cart.
import { ErrorResponse } from "@Composable Commerce/platform-sdk";


// Code to create an array of CartUpdateAction for example addLineItemAction, removeLineItemAction
try {
    const response = await apiRoot
        .inStoreKeyWithStoreKeyValue({ storeKey })
        .carts()
        .withId({ ID: cartId })
        .post({ body: { version, actions } })
        .execute();
} catch (error) {
    const errorResponse = error as ErrorResponse;
    if (errorResponse.statusCode === 409) {
        // Version conflict. Handle error for example Handle by using the version number in the error body here or do this in the middleware layer.
    } else {
       // Handle other errors
     }
}

Handle Not Found and Authorization errors

When fetching a Cart or creating one, it’s possible the requested Cart might not exist or belong to that Store, or the user does not have access.

import { ErrorResponse } from from "@Composable Commerce/platform-sdk";

// Existing code
try {
    const response = await apiRoot
        .inStoreKeyWithStoreKeyValue({ storeKey })
        .carts()
        .withId({ ID: cartId })
        .get()
        .execute();
} catch (error) {
    const errorResponse = error as ErrorResponse;
    if (errorResponse.statusCode === 404) {
           // Cart not found. Handle error for example return undefined or rethrow the error and handle it on a higher level in the call stack.
     } else if (errorResponse.statusCode === 403) {
          // Access denied to Cart, API Client doesn't have permission to access the Cart
     } else () {
   // Handle other errors
     }
}

Security: validate Cart ownership

A critical security measure is to ensure you only return a Cart to its rightful owner. Fetching a Cart by its id alone does not validate ownership. A malicious actor could potentially guess Cart IDs to view another user's Cart contents.
After successfully fetching a Cart, your backend application must verify that its customerId (for logged-in users) or anonymousId (for guest users) matches the identifier of the user making the request.
Recommended verification flow:
  1. The frontend makes a request to your BFF API to get the Cart.
  2. Your API authenticates the user and gets their userId or anonymousId from their session/token.
  3. Your API fetches the Cart from Composable Commerce using the cartId.
  4. Crucially, your API compares the cart.body.customerId or cart.body.anonymousId with the user's ID from the session.
  5. If they match, return the Cart.
  6. If they do not match, treat it as a 404 Not Found to avoid leaking information that the Cart exists. Log this as a potential security event.

API design: communicating state changes and partial success

When a user modifies their Cart, the frontend may need to provide specific feedback, such as "Item X was added to your Cart" or "The quantity for Item Y was updated." Simply receiving the updated Cart object from the API is not enough, as the frontend doesn't automatically know what changed between the update and the last version of the Cart.

The BFF's primary responsibility is to bridge this gap. It must inspect the client's request, observe the result from the Composable Commerce API, and return a structured response that explicitly describes the outcome of each requested change.

This pattern involves reconciling the initial intent (the actions array) with the final outcome (the updated Cart and the errors array from the API response).
Recommended reconciliation pattern:
  1. Receive and retain: the BFF receives a request from the client containing the list of actions to perform. The BFF must keep this list in memory for the next step.
  2. Execute: the BFF sends the actions to the Composable Commerce API.
  3. Analyze the response: the BFF must inspect both the updated cart object and the errors array in the response body.
  4. Reconcile and build response: the BFF iterates through the original actions it received from the client and any actions it added. For each action, it determines if it succeeded or failed.
  5. Construct a rich response: the BFF sends a response to the client that includes:
    • The final, updated cart object with the necessary field.
    • A structured changes or messages object that clearly lists the successful and failed operations, providing enough detail for the UI to display meaningful notifications.

Test your knowledge