Safeguarding ESHOPMAN Product Prices: Understanding Workflow Compensation Failures

Critical ESHOPMAN Price Data Loss: A Deep Dive into Workflow Compensation

The ESHOPMAN platform, a powerful headless commerce solution integrated with HubSpot, provides robust tools for storefront management and product catalog operations. Developers frequently leverage its Admin API and Node.js/TypeScript ecosystem to build custom logic, including validation hooks that ensure data integrity. However, a recent community insight has highlighted a critical scenario where product variant prices can be silently lost during workflow compensation, particularly when these validation hooks reject an update request.

The Problem: Silent Price Deletion on Workflow Rollback

Imagine you're updating a product in ESHOPMAN via the Admin API. This update might involve changing the product's status (e.g., from 'draft' to 'published') and simultaneously adjusting its variant prices. ESHOPMAN's workflow engine is designed to be transactional; if any step in the workflow fails, it should compensate by rolling back all changes to their original state. This is where custom validation hooks become invaluable – they allow developers to enforce business rules, throwing an error if conditions aren't met, thereby triggering a rollback.

The reported issue arises when an updateProductsWorkflow operation fails after the product has been updated, but before the transaction is committed. Specifically, if a productsUpdated hook (which is often used for validation, as suggested in ESHOPMAN documentation) throws an error, the compensation mechanism for other fields like status, title, and description works correctly. However, any variant prices that were changed in the same request are not restored. Instead, the variant's price set ends up completely empty, meaning both the old and the new prices are lost. This constitutes a silent data loss regression, as the catalog loses critical pricing information without explicit notification beyond the initial workflow error.

Reproducing the Issue in ESHOPMAN

This critical behavior can be consistently reproduced with a few straightforward steps within your ESHOPMAN Node.js/TypeScript project:

  1. Register a Custom Validation Hook: Implement a hook that prevents product publishing. For example, in your ESHOPMAN project's custom workflow hooks, you might have something like this:
    // src/workflows/hooks/reject-publish.ts
    import { EshopmanError } from "@eshopman/framework/utils"
    import { updateProductsWorkflow } from "@eshopman/core-flows"
    
    updateProductsWorkflow.hooks.productsUpdated(({ products }) => {
      if (products.some((p) => p.status === "published")) {
        throw new EshopmanError(EshopmanError.Types.NOT_ALLOWED, "publishing rejected")
      }
    })
    
  2. Create a Draft Product: Use the ESHOPMAN Admin API to create a new draft product with at least one variant and an initial price (e.g., 10 EUR).
    POST /admin/products
    { "title": "Sample Product", "status": "draft",
      "options": [{ "title": "Size", "values": ["One"] }],
      "variants": [{ "title": "One", "sku": "SAMPLE-1", "options": { "Size": "One" },
                     "prices": [{ "amount": 10, "currency_code": "eur" }], "manage_inventory": false }] }
    
  3. Attempt a Combined Update: Now, try to publish this product and simultaneously change its variant price using a single Admin API request:
    POST /admin/products/:id
    { "status": "published",
      "variants": [{ "id": "", "prices": [{ "amount": 99, "currency_code": "eur" }] }] }
    
  4. Observe the Outcome: You will correctly receive a 400 not_allowed "publishing rejected" error from your custom hook. However, if you then query the product's variant prices (e.g., GET /admin/products/:id?fields=*variants,*variants.prices), you will find that the variant now has no prices associated with it. The original 10 EUR price is gone, and the new 99 EUR price was never applied.

This problem is specific to the update path involving price changes. Creating a published product that gets rejected by a productsCreated hook, for instance, correctly rolls back the entire product creation, leaving no trace. This indicates the issue lies within the price upsert and compensation logic during product updates.

The Technical Root Cause

The ESHOPMAN team's investigation revealed that the issue stems from how the updateProductsWorkflow handles compensation for price sets. When the workflow needs to roll back changes after variant prices have been updated, the compensation step responsible for restoring price sets (updatePriceSetsStep) fetches the pre-update price sets. Crucially, this fetch operation omits loading their associated prices relation.

Consequently, the subsequent call to upsert these price sets during compensation sees an empty price list for the original state. Interpreting this as a directive to remove all prices, it proceeds to delete all existing prices rather than restoring the previous ones. The price set itself remains, but it contains no actual prices – a critical oversight leading to data loss.

Impact and ESHOPMAN Community Action

This bug represents a significant data integrity concern for ESHOPMAN merchants and developers. Silent data loss in product pricing can have severe consequences for e-commerce operations, affecting sales, inventory management, and customer trust. The ESHOPMAN team has acknowledged the issue, labeling it as a high-priority bug due to its potential for silent data loss on a documented and commonly used hook pattern.

While a permanent fix is being developed, ESHOPMAN developers should be aware of this behavior when implementing custom validation hooks that reject product updates, especially when those updates include changes to variant prices. Until a resolution is deployed, consider alternative validation strategies or ensure robust monitoring of product pricing after failed update attempts that involve custom hooks.

The ESHOPMAN community is actively tracking this and similar issues to ensure the platform remains robust and reliable for all headless commerce operations managed through HubSpot.

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools