Skip to content

The Shopify Event With No Subscriber (What I Found Auditing My Own Build)

CONVERSION TRACKING
The Shopify Event With No Subscriber (What I Found Auditing My Own Build)
Conner Crowe

I ran a tracking audit on a Shopify storefront I had built, against the live API and the rendered page rather than against my own build notes. The primary lead conversion was in the account, marked Primary, counting. Its conversion label appeared nowhere on the storefront. The form fired an event on every successful submission. Nothing was listening. No paid traffic had run through it yet, which is the only reason this is a post about a defect instead of a post about a wasted budget.

Quick Take

Shopify lets a theme file publish a custom event with Shopify.analytics.publish(). It does not require anything to subscribe to that event. If no pixel subscribes, the call succeeds, the browser reports no error, and the event evaporates. My snippet published on a successful form post and waited for a web pixel to forward it to Google Ads. Neither of the two app pixels installed on the store had any reason to listen for a string I had invented. Check that something is listening before you trust the thing that sends. A publisher with no subscriber is not tracking.

How the Gap Opens

Shopify’s Web Pixels API has two halves that are documented separately and shipped separately.

The publish half lives in the theme. From a Liquid file you can call Shopify.analytics.publish('my_store:event_name', event_data), and the platform accepts it. That call is fire and forget. It does not return a count of listeners, and it does not warn you when the count is zero.

The subscribe half lives somewhere else entirely, in the admin under Customer events, where a pixel calls analytics.subscribe('my_app:my_custom_event', callback) and receives your payload on the customData field. Nothing binds the two halves at build time. They get matched by string at runtime, inside a sandbox. From the theme’s side a miss looks exactly like a hit.

That is the whole failure. It is a quieter cousin of the mismatch I describe in why Shopify conversion tracking stops working, where a pixel emits add_to_cart and a Tag Manager trigger listens for addToCart. In that version a consumer exists and the string is wrong. In mine no consumer had been written at all. The theme half of the work looks finished either way, and both fail without an error.

I should say why I reached for the sandbox at all, because that is where the defect starts. The lead form is a theme page, not checkout. It never needed the Web Pixels sandbox. I wired it that way out of habit from checkout work, where the sandbox is the only option, which is the scope I set out in what Checkout Extensibility breaks.

The Audit Path

I stopped reading my own build notes and searched the rendered storefront for the conversion label string. Zero occurrences. The label existed in Google Ads and existed nowhere in the HTML the browser receives.

Then I opened the snippet. It fired on a successful post, exactly as designed, and published a neutral custom event. Then Shopify admin → Settings → Customer events. Two app pixels listed. One does session recording and the other fires its own standard ecommerce events, so neither had any reason to subscribe to a custom event name I had made up, and neither carried the conversion label it would have needed to forward it correctly if it had.

The same pass turned up two more conversion actions counting nothing, one duplicating a purchase that already had a working action and one pointed at a page no visitor could reach. Both went to Secondary, on the source-emission and tag-trigger layers of the STACK Audit that I run on every inherited account and had not yet run on my own.

The pattern across all three is the same one I found in my own Tag Manager container in May. Primary, in the Google Ads interface, describes a row in Google’s database. It says nothing about whether the label exists in your HTML. My pre-launch sweep on this build covered copy and schema, and measurement was not in that pass, which is how a dead conversion cleared a checklist I wrote myself.

The Rewire, and Why I Did Not Add a Tag

The fix was to stop routing a theme-page conversion through the pixel sandbox and fire it on the page.

Google documents this directly. Its custom pixel limitations page says that running Google tags inside Shopify’s custom pixel feature is not a supported implementation, and names what may not work correctly from that sandbox, including enhanced conversions, phone call conversions, consent mode URL passthrough, cross-domain measurement, conversion verification and troubleshooting, and setting up conversions with a URL. Enhanced conversions matter most for a lead form, because the hashed email the form already collects is the whole reason to collect it. Google’s recommendation for sitewide measurement is its own Google & YouTube app.

Before writing anything I measured what the page already had. On this store the app had injected gtag into the main document and configured the Ads conversion ID with send_page_view: false, and the Google click identifier cookie was already being set first party. A second Google tag on top of that would have fought the app and risked double counting, which is the failure I would have created while fixing the first one.

So the snippet stopped publishing into the sandbox and started firing through the queue that was already there:

// Before: published into the Web Pixels sandbox, where nothing subscribed.
Shopify.analytics.publish('my_store:enquiry_submitted', { value: 1 });

// After: fire on the page, reusing the gtag the Google & YouTube app installed.
window.dataLayer = window.dataLayer || [];
window.gtag = window.gtag || function () { window.dataLayer.push(arguments); };
gtag('event', 'conversion', { send_to: 'AW-XXXXXXXXXXX/YourConversionLabel' });

The || on that middle line is the part that matters. It reuses the app’s gtag if one is present and only defines a shim when it is not, so the store still runs one Google tag rather than two fighting over the same conversion.

I verified it the way I verify any of these. Fire a real submission, watch the network, confirm the request to googleadservices.com/pagead/conversion/ leaves the browser carrying the conversion label. A 200 on that beacon is the first evidence that any of it works. Before that, all I have is my own build notes, which is what put me here in the first place.

What This Shape of Defect Costs

An account in this state does not look broken. The conversion action reads Primary and counted. The pixels are listed under Customer events. The storefront serves fine. Every surface a person checks in a hurry is green.

Then a budget goes live on an account in this state, and Smart Bidding optimizes toward a conversion it will never observe. Maximize conversions with no observable conversions spends the budget on whatever proxy the auction offers, and the account’s own reporting agrees with the bid strategy that nothing is happening. The damage compounds, because the first read of flat conversion volume is usually a media problem, so the next month goes to keywords and creative while the measurement layer stays dead underneath. The contractor whose only website conversion had been dead since somebody renamed a form button is the version of this that ran for months before anyone found it.

If you are the founder paying for this rather than the person building it, the question to ask whoever owns your stack is not whether tracking is set up. It is what evidence they have that the conversion left the browser, and when they last looked. A screenshot of a green conversion row is not that evidence. A network capture of the beacon is.

What I did not claim

Any recovery. No paid traffic has run through this account, so there is no lost-conversion figure, no before-and-after, and no performance result to point at. I cannot tell you what the defect would have cost and I am not going to estimate it.

I also did not claim the account is clean now. Enhanced conversions for leads is not live end to end. Two items on my own audit list are marked unverified rather than passed, for the access reason below.

What is claimed is narrow and checkable: a Shopify theme file can publish a custom event that nothing subscribes to, the platform raises no error when that happens, and the conversion will read Primary and counted in Google Ads while recording zero. The defect was in my own build, it survived my own build notes, and it took reading the page instead of the notes to find it.

The Receipts

The audit ran through the Google Ads API and against the rendered storefront rather than through the Ads interface, because the browser profile signed in at the time was the wrong account. That is a real limitation and it is why two items on the list, the auto-apply recommendation settings and the GA4 key event mappings, are marked unverified rather than passed.

Fixed and confirmed: the lead conversion beacon reaches googleadservices.com/pagead/conversion/ carrying its label. Applied and re-read after writing: two conversion actions demoted from Primary to Secondary, leaving one Primary purchase and one Primary lead. Enhanced conversions for leads is not yet live end to end, and I am not claiming it.

One correction to my own first pass, since it belongs here. I flagged an Add Payment Info action as miscategorized and withdrew it before applying anything. There is no add-payment-info value in the conversion category enum, which I confirmed by reading the enum live. The app had it right and I had it wrong.

The client is unnamed by agreement.

Keep going

If this hit, the next two pieces in the same universe:

Open your storefront’s rendered HTML and search it for your conversion label. If the string is not there, the conversion is not there, whatever the account says.

The checkout-extensibility half of this, what it breaks and the order to fix it in, is at Shopify Checkout Extensibility and tracking.

Free PDF: The 25-page Tracking Stack. Layer 1 is source emission, which is the layer this defect lived on, and it covers the post-2025 Shopify checkout extensibility audit that decides which events belong in the sandbox and which do not. The layer map is at /frameworks/tracking-stack. No email gate.

Want a review like this on your account?

Want this kind of review
on your account?

Thirty minutes on the phone. Same person on the call as on the work. Walk out with a clear set of next steps.