How to use expand operation in Cart Transform function correctly ?

Topic summary

Developers are experiencing issues with the Cart Transform function’s expand operation not working as expected. The original poster implemented a function to expand bundle products into component parts based on metafield data, with the function successfully returning expand operations in the output (showing two product variants with fixed prices). However, when adding a product with ID 49268939227432 to the cart, the expected companion item (ID 49268939260200) doesn’t appear in the actual cart, despite the function output indicating it should.

Key Technical Details:

  • Function logs show successful execution with proper operation structure
  • Output includes expandedCartItems array with merchandise IDs and pricing adjustments
  • The disconnect occurs between function output and actual cart behavior

Troubleshooting Attempts:

  • One user confirmed they created the cartTransform object via the Admin GraphQL API
  • Local testing shows “success” status with proper function execution
  • The issue remains unresolved, with multiple users encountering the same problem

The discussion is ongoing with no definitive solution identified yet.

Summarized with AI on October 24. AI used: claude-sonnet-4-5-20250929.
// @ts-check

/*
A straightforward example of a function that expands a bundle into its component parts.
The parts of a bundle are stored in a metafield on the product parent value with a specific format,
specifying each part's quantity and variant.

The function reads the cart. Any item containing the metafield that specifies the bundle parts
will return an Expand operation containing the parts.
*/

/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 * @typedef {import("../generated/api").CartOperation} CartOperation
 */

/**
 * @type {FunctionRunResult}
 */
const NO_CHANGES = {
  operations: [],
};

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const operations = input.cart.lines.reduce(
    /** @param {CartOperation[]} acc */
    (acc, cartLine) => {
      const expandOperation = optionallyBuildExpandOperation(cartLine, input);

      if (expandOperation) {
        return [...acc, { expand: expandOperation }];
      }

      return acc;
    },
    []
  );

  return operations.length > 0 ? { operations } : NO_CHANGES;
}

/**
 * @param {RunInput['cart']['lines'][number]} cartLine
 * @param {RunInput} input
 */
function optionallyBuildExpandOperation(
  { id: cartLineId, merchandise, cost, addonsAdded },
  { cartTransform: { addonsVariantID }, presentmentCurrencyRate }
) {
  const hasWarrantyMetafields =
    merchandise.__typename === "ProductVariant" &&
    !!merchandise.discountPercentage &&
    !!addonsVariantID;
  const shouldAddWarranty = addonsAdded?.value === "Yes";
  const discountPercentage =
    merchandise.__typename === "ProductVariant" &&
    merchandise.discountPercentage?.value
      ? parseFloat(merchandise.discountPercentage.value)
      : 0;

  if (
    merchandise.__typename === "ProductVariant" &&
    hasWarrantyMetafields &&
    shouldAddWarranty
  ) {
    return {
      cartLineId,
      title: `${merchandise.title} with discount`,
      // Optionally override the image for line item
      // image: { url: "https://cdn.shopify.com/.../something.png" },
      expandedCartItems: [
        {
          merchandiseId: addonsVariantID.value,
          quantity: 1,
          price: {
            adjustment: {
              fixedPricePerUnit: {
                amount: (
                  cost.amountPerQuantity.amount *
                  ((100 - discountPercentage) / 100) *
                  presentmentCurrencyRate
                ).toFixed(2),
              },
            },
          },
        },
        {
          merchandiseId: merchandise.id,
          quantity: 1,
          price: {
            adjustment: {
              fixedPricePerUnit: {
                amount: (
                  cost.amountPerQuantity.amount *
                  ((100 - discountPercentage * 2)  / 100) *
                  presentmentCurrencyRate
                ).toFixed(2),
              },
            },
          },
        },
      ],
    };
  }

  return null;
}
query RunInput {
  presentmentCurrencyRate
  cart {
    lines {
      id
      quantity
      cost {
        amountPerQuantity {
          amount
          currencyCode
        }
      }
      # Access the cart line attribute to decide if we should add a warranty
      addonsAdded: attribute(key: "addons_added") {
        value
      }
      merchandise {
        __typename
        ... on ProductVariant {
          id
          title
          discountPercentage: metafield(namespace: "$app:add-ons", key: "add_ons") {
            value
          }
        }
      }
    }
  }
  cartTransform {
    # Access the variant ID that represents the warranty product
    addonsVariantID: metafield(namespace: "$app:add-ons", key: "add_ons") {
      value
    }
  }
}

the output

{
  "operations": [
    {
      "expand": {
        "cartLineId": "gid://shopify/CartLine/6227da72-02f3-4dc5-b4e1-1ccdbaf9a176",
        "title": "Selling Plans Ski Wax with discount",
        "expandedCartItems": [
          {
            "merchandiseId": "gid://shopify/ProductVariant/49268939260200",
            "quantity": 1,
            "price": {
              "adjustment": {
                "fixedPricePerUnit": {
                  "amount": "22.50"
                }
              }
            }
          },
          {
            "merchandiseId": "gid://shopify/ProductVariant/49268939227432",
            "quantity": 1,
            "price": {
              "adjustment": {
                "fixedPricePerUnit": {
                  "amount": "20.00"
                }
              }
            }
          }
        ]
      }
    }
  ]
}

I picked one with id=49268939227432 and then added it to the cart, in the cart I expect another one with id=49268939260200 as I process in function but I can’t see

Hi @Cheesedz ,

Have you figure this out ? I’m running into the same issue

Best,

Perhaps you have not created cartTransform object

I did, I’m even seeing the function logs when running it locally as Sucess:

      "operations": [
        {
          "lineExpand": {
            "cartLineId": "gid://shopify/CartLine/a2293265-837d-407a-aa2d-e741ce2453a7",
            "expandedCartItems": [
              {
                "merchandiseId": "gid://shopify/ProductVariant/40743500578914",
                "quantity": 1
              }
            ]
          }
        }
      ]
    },
    "outputBytes": 187,
    "logs": [],
    "functionId": "0199528c-d14e-714d-8a36-5164b29f9e9e",
    "fuelConsumed": 240540
  },
  "logType": "function_run",
  "status": "success",
  "source": "bundle-builder",
  "sourceNamespace": "extensions",
  "logTimestamp": "2025-09-17T09:59:27.632601Z",
  "localTime": "2025-09-17 11:59:27",
  "storeName": "lucass-development-store.myshopify.com"
}