How can I fix a cart transform function error in a custom bundle app?

Topic summary

A developer is building a custom Shopify bundle app and encountering a cart transform function error. The extension’s documentation only covers Rust, making JavaScript implementation challenging.

The Problem:

  • Error message: “Field is not defined on CartLineInput” for merchandise_id field
  • The output incorrectly duplicates the gid://shopify/ProductVariant/ prefix when mapping component references
  • Current code produces malformed merchandise IDs like "gid://shopify/ProductVariant/gid://shopify/ProductVariant/43702370304153"

Current Implementation:

  • Reads bundle component references from a metafield (component_reference) as JSON array
  • Attempts to expand bundle parent items into individual cart lines using a merge operation
  • Uses incorrect field name merchandise_id instead of the expected structure

Expected Output Format:
Another user (MastersHub) provided a sample showing the correct structure should use:

  • cartLineId (not cart_line_id)
  • parentVariantId with pricing/title metadata
  • Proper nesting without duplicated GID prefixes

The issue appears to stem from incorrect field naming and improper string concatenation when building merchandise IDs from the parsed component references.

Summarized with AI on November 13. AI used: claude-sonnet-4-5-20250929.

I’m currently trying to build a custom bundle app - but I’m encountering issues with the extension’s output. Unfortunately, the only docs they have for custom bundles are in Rust language - which I need to be more comfortable in for a production app. I appreciate any help/input, below I’ve attached the input/output/error of the extension runs and the source code of both the run.js and the run.graphql

Input

{
  "cart": {
    "lines": [
      {
        "id": "gid://shopify/CartLine/17d66918-72fc-4f23-9b67-8105c4e85e4b",
        "quantity": 1,
        "merchandise": {
          "__typename": "ProductVariant",
          "id": "gid://shopify/ProductVariant/43739736604825",
          "component_reference": {
            "value": "[\"gid://shopify/ProductVariant/43702370304153\",\"gid://shopify/ProductVariant/43738015236249\",\"gid://shopify/ProductVariant/43738015334553\",\"gid://shopify/ProductVariant/43738015465625\"]"
          }
        }
      }
    ]
  }
}

Output:

{
  "operations": [
    {
      "merge": {
        "parentVariantId": "gid://shopify/ProductVariant/43739736604825",
        "cartLines": [
          {
            "cart_line_id": "gid://shopify/CartLine/17d66918-72fc-4f23-9b67-8105c4e85e4b",
            "quantity": 1,
            "merchandise_id": [
              "gid://shopify/ProductVariant/gid://shopify/ProductVariant/43702370304153",
              "gid://shopify/ProductVariant/gid://shopify/ProductVariant/43738015236249",
              "gid://shopify/ProductVariant/gid://shopify/ProductVariant/43738015334553",
              "gid://shopify/ProductVariant/gid://shopify/ProductVariant/43738015465625"
            ]
          }
        ],
        "title": "Mix Merge"
      }
    }
  ]
}

Error:

[
  {
    "path": [
      "operations",
      0,
      "merge",
      "cartLines",
      0,
      "cartLineId"
    ],
    "explanation": "Expected value to not be null"
  },
  {
    "path": [
      "operations",
      0,
      "merge",
      "cartLines",
      0,
      "cart_line_id"
    ],
    "explanation": "Field is not defined on CartLineInput"
  },
  {
    "path": [
      "operations",
      0,
      "merge",
      "cartLines",
      0,
      "merchandise_id"
    ],
    "explanation": "Field is not defined on CartLineInput"
  }
]

Source Run.js

// -check

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

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

/**
 *  {RunInput} input
 * @returns {FunctionRunResult}
 */
function run(input) {
  const cartOperations = getExpandCartOperations(input.cart);

  return {
    operations: cartOperations,
  };
}

function getExpandCartOperations(cart) {
  const result = [];

  for (const line of cart.lines) {
    const variant = line.merchandise;

    if (!variant || variant.__typename !== "ProductVariant") {
      continue;
    }

    const componentReferences = getComponentReferences(variant);

    if (componentReferences.length === 0) {
      continue;
    }

    const expandedCartItems = componentReferences.map((reference) => ({
      merchandise_id: `gid://shopify/ProductVariant/${reference}`,
      quantity: 1,
    }));

    const expandOperation = {
      merge: {
        parentVariantId: `gid://shopify/ProductVariant/${variant.id}`,
        cartLines: componentReferences.map((reference) => ({
          cartLineId: line.id, // use the line id here
          merchandise: {
            id: `gid://shopify/ProductVariant/${reference}`,
          },
          quantity: 1,
        })),
        title: "Mix Merge",
      },
    };
    
    result.push(expandOperation);
  }

  return result;
}

function getComponentReferences(variant) {
  if (variant.component_reference) {
    return JSON.parse(variant.component_reference.value);
  }

  return [];
}

Source run.graphql

query Input {
  cart {
    lines {
      id
      quantity
      merchandise {
        __typename
        ... on ProductVariant {
          id
          component_reference: metafield(
            namespace: "custom"
            key: "component_reference"
          ) {
            value
          }
        }
      }
    }
  }
}

Sample output should be like the one below.

{
“operations”: [
{
“merge”: {
“cartLines”: [
{
“cartLineId”: “gid://shopify/CartLine/2”,
“quantity”: 1
},
{
“cartLineId”: “gid://shopify/CartLine/3”,
“quantity”: 1
},
{
“cartLineId”: “gid://shopify/CartLine/4”,
“quantity”: 1
}
],
“parentVariantId”: “gid://shopify/ProductVariant/123”,
“price”: {
“percentageDecrease”: {
“value”: 10.5
}
},
“title”: “Meal Kit”,
“image”: {
“url”: “https://cdn.shopify.com/[…]/custom-image.png
}
}
}
]
}