Summary
A cart_transform Shopify Function correctly computes and returns a linesMerge operation (confirmed via Shopify’s own function-run logs), the cart transform is activated for the shop, but the resulting cart lines are never actually merged — not in the cart page, not in /cart.js, not at checkout, and not in the final completed order.
Environment
Shop: restockpulse-dev.myshopify.com — confirmed a genuine Shopify Partner development store, not just a store with “-dev” in the name: shop.plan.partnerDevelopment: true, shop.plan.displayName: “Basic App Development”, shop.plan.shopifyPlus: false (queried directly via the Admin GraphQL API). Ruling this out matters because Shopify Plus/development-store exemptions apply to lineUpdate specifically, not the linesMerge operation this report is about.
App version: deployed and released via shopify app deploy. Extension: cart_transform type, target cart.transform.run. Cart transform: created via cartTransformCreate, userErrors: . Function API version: 2026-04. Access scopes include write_cart_transforms.
Function code
src/cart_transform_run.graphql:
query CartTransformRunInput {
cart {
lines {
id
quantity
groupAttribute: attribute(key: “_sheafly_bundle_group”) {
value
}
}
}
}
src/cart_transform_run.ts:
import type {
CartTransformRunInput,
CartTransformRunResult,
} from “../generated/api”;
const NO_CHANGES: CartTransformRunResult = {
operations: ,
};
const TEST_PARENT_VARIANT_ID = “gid://shopify/ProductVariant/52570730725508”;
// requiresComponents: true, one placeholder component
export function cartTransformRun(
input: CartTransformRunInput,
): CartTransformRunResult {
const groups = new Map();
for (const line of input.cart.lines) {
const groupId = line.groupAttribute?.value;
if (!groupId) continue;
const existing = groups.get(groupId) ?? ;
existing.push({ id: line.id, quantity: line.quantity });
groups.set(groupId, existing);
}
const operations = ;
for (const [groupId, lines] of groups) {
if (lines.length < 2) continue;
operations.push({
linesMerge: {
cartLines: lines.map((l) => ({
cartLineId: l.id,
quantity: l.quantity,
})),
parentVariantId: TEST_PARENT_VARIANT_ID,
title: Mix and match spike (${groupId}),
attributes: [{ key: “_sheafly_bundle_group”, value: groupId }],
},
});
}
return operations.length > 0 ? { operations } : NO_CHANGES;
}
Steps to reproduce
Add two different product variants to the cart, each with the same _sheafly_bundle_group line item property (e.g. via /cart/add.js with properties). Then view /cart.js, the cart page, or checkout.
Expected
One merged bundle line (per linesMerge semantics — title/parent variant from the operation, two components).
Actual
Two separate, unmerged line items — every time, at every stage, including the completed order.
Evidence the function itself is working correctly
Captured Shopify function-run log (every invocation during testing looked like this):
{
“payload”: {
“export”: “cart-transform-run”,
“input”: {
“cart”: {
“lines”: [
{ “id”: “gid://shopify/CartLine/…”, “quantity”: 1, “groupAttribute”: { “value”: “test-group-3” } },
{ “id”: “gid://shopify/CartLine/…”, “quantity”: 1, “groupAttribute”: { “value”: “test-group-3” } }
]
}
},
“output”: {
“operations”: [
{
“linesMerge”: {
“cartLines”: [
{ “cartLineId”: “gid://shopify/CartLine/…”, “quantity”: 1 },
{ “cartLineId”: “gid://shopify/CartLine/…”, “quantity”: 1 }
],
“parentVariantId”: “gid://shopify/ProductVariant/52570730725508”,
“title”: “Mix and match spike (test-group-3)”,
“attributes”: [{ “key”: “_sheafly_bundle_group”, “value”: “test-group-3” }]
}
}
]
},
“functionId”: “019fa87f-22c8-7b9d-a065-62743a328b95”,
“target”: “cart.transform.run”
},
“status”: “success”
}
The input and output are exactly correct for the merge to happen. It’s the actual merge that never occurs.
What was ruled out before filing this
Conflicting bundle-shell variant: first attempt reused an existing bundle already wired to different real components (productVariantComponents); switched to a dedicated, freshly-created shell variant with a single placeholder component. No change.
Local dev-preview artifact: ran shopify app deploy to create and release a real app version, stopped shopify app dev entirely so the storefront ran purely on the deployed release, re-activated the transform against the deployed function ID. No change.
Stale cart/checkout session: repeatedly cleared cookies and cart state, confirmed genuinely new checkout tokens each attempt. No change.
Only visible at order completion, not mid-checkout: completed a full real test order (Bogus Gateway test card 1, no real charge). Order still shows two separate line items (“The Collection Snowboard: Hydrogen” $600 x 1, “The Collection Snowboard: Oxygen” $1,025 x 1), each independently carrying its own _sheafly_bundle_group property, not one merged bundle line.
Question
linesMerge is accepted with no errors, and the function computes the exact operation Shopify’s own docs describe every single time (confirmed by the captured function-run logs above) — but the merge never actually happens, all the way through to a completed order. This isn’t a Plus/development-store plan issue (confirmed the shop is a genuine Partner development store, and that restriction applies to lineUpdate, not linesMerge, in any case).
Is there a step missing here that isn’t apparent from the docs — or is this a genuine bug in how linesMerge is applied?
Title (separate field, not part of the body): Cart Transform linesMerge accepted with no errors, but merge never applies (confirmed even in a completed order)