Hunting Down a Shopify Metafields Liquid Failure
Walk through a shopify metafields liquid audit: where blank output comes from, why .value fixes most of it, and the five things to check first.

Across 14 metafield audits I worked through this spring, twelve traced back to a single character or a single missing word. The actual metafield was fine each time. The Liquid call reading it was off by one detail, and the storefront rendered nothing.
This post walks through one of those audits in real time. The merchant had been at it for a week. I knew the fix in five minutes. The reason I knew it that fast is the reason I'm writing this down: the failure mode is small, common, and almost never named in the docs the merchant had been reading.
Quick Answer
The most common reason a shopify metafields liquid call comes back blank is that the call is reading the metafield's wrapper object instead of its data. The fix is one word: .value. The second most common reason is variable scope inside Custom Liquid blocks. Almost everything else I see is one of those two with a slight twist.
What this merchant brought me
She was on Horizon, sold roasted coffee, and wanted to display three "tasting notes" per product. She'd built a metaobject definition called tasting-note with fields for name, intensity, and a short description. She'd assigned three of them to each product. On the product page, her Custom Liquid block was supposed to render them as a tidy list with the name in bold and the description after.
What rendered was:
gid://shopify/Metaobject/87122415872
gid://shopify/Metaobject/87122416128
gid://shopify/Metaobject/87122416384
Three GID strings. No names. No descriptions.
She had asked Claude and ChatGPT for help. She'd watched two YouTube tutorials, both of which made the syntax look easy. She'd checked her field assignments three times. The product editor displayed all three tasting notes correctly. The storefront just kept printing the GIDs.
Her Liquid:
{% for note in product.metafields.custom.tasting_notes %}
- {{ note.name }}: {{ note.description }}
{% endfor %}
What I checked first
My first instinct on any metafield issue is to confirm the data is actually entered. Definitions are easy to set up; entries get skipped. So I opened the product in admin, scrolled to the Metafields section, and saw three tasting notes attached. Data was there.
Next: the metafield type. The definition was "list of metaobjects" pointing at the correct metaobject reference. That looked right too.
Next: storefront access. Shopify's metafield definitions have an access setting that controls whether the storefront can read the field. I checked Settings, Custom data, the definition, and the access toggle was on.
Three obvious things checked. Three passes. And the output was still GID strings.
Where the trail went cold
I tried two diagnostics that did not move me forward.
First, a guard around the loop to confirm Liquid was actually entering it:
{% if product.metafields.custom.tasting_notes %}
Found the metafield
{% for note in product.metafields.custom.tasting_notes %}
- {{ note.name }}: {{ note.description }}
{% endfor %}
{% endif %}
The page printed "Found the metafield" followed by the same three GID strings. So the metafield existed. The loop ran. Inside the loop, every iteration produced a GID instead of the data the merchant wanted.
Second, I tried {{ product.metafields.custom.tasting_notes | json }} to inspect the object's structure. It returned "error":"json not allowed for this object". Shopify's json filter does not work on metafield objects of certain types, so the usual shopify metafields liquid debug path was closed.
I was about to spin up a partner dev store and try to reproduce the failure cleanly. That's usually the next step when an audit hits a wall.
The clue I almost missed
Before I built a repro, I went back to the merchant's Liquid one more time and stared at the access path: product.metafields.custom.tasting_notes.
The thing I do every time at this point - and forget a third of the time - is to ask what type of value comes back from that exact path. A direct access on a metafield does not give you the data. It gives you the metafield wrapper object. The wrapper holds metadata (the type, the namespace, the key) and a .value property that holds the actual data. For a list-of-references metafield, iterating the wrapper walks the underlying GID references. Iterating .value walks the resolved metaobjects.
So product.metafields.custom.tasting_notes produced a wrapper. The for loop over that wrapper iterated the underlying GID references. To loop over the resolved metaobjects, the path needed .value.
That was the whole chase. One missed property. A week of the merchant's debugging time, four AI conversations, and two YouTube videos distilled to a four-character fix:
{% for note in product.metafields.custom.tasting_notes.value %}
- {{ note.name.value }}: {{ note.description.value }}
{% endfor %}
Three .values, actually. One on the outer loop reference, one each on the metaobject's own fields. Every field of a metaobject is itself wrapped. The pattern recurses.
The page rendered correctly on the next save.
Why this kept happening across other stores
The reason I knew this fix in five minutes is that I'd already debugged ten variations of the same root cause. Different metafield types, identical pattern:
- A merchant with a link-list metafield got an empty page from her for-loop. Missing
.valueon the metafield reference. - A jeweler with a
materialsmetafield gotgid://shopify/Metaobject/...strings instead of material names. Same pattern, different surface. - A merchant with a single-line text
roast-imagemetafield got a literal["Dark"]instead of the word "Dark". When a single-line text metafield is rendered without.value, Shopify outputs the JSON-array form of the underlying storage.
The .value rule is the single shopify metafields liquid fact I wish Shopify wrote in a banner at the top of every metafield documentation page. It is the difference between code that runs and produces garbage and code that runs and produces what you wanted.
There is one common case where the fix is something else. Custom Liquid blocks dropped into product card slots on a Horizon homepage do not have access to the card_product variable that the theme's internal product-card snippet uses. The block needs closest.product instead. AI tools generate card_product because they were trained on theme-snippet code, not user-authored block code. If your Custom Liquid block on the homepage isn't rendering and the same code works on the product page, that variable is what to check first. I wrote a separate post about picking the right AI tool for Shopify theme work; the short version applies here too: the model is fine as a coding partner, less reliable as a context-aware Shopify expert.
The third pattern I see often is Horizon's missing "list of product references" input type in its native blocks. If you're trying to surface a metafield of that type through the dynamic-content dropdown and the metafield isn't showing up, the dropdown isn't broken and the metafield is fine. The block just doesn't accept that input type. Three separate community threads from mid-2025 through early 2026 document the same gap. The workaround is a Custom Liquid section with a for-loop, which is exactly the kind of code most merchants installed Shopify to avoid writing.
What I now check before I touch a line of Liquid
After watching too many of these audits chew up an afternoon, I built a five-item checklist. I run through it before opening the theme editor:
- Does the metafield actually have data on this product? Open the product in admin, scroll to the bottom, look at the Metafields section. If the field is listed but blank, no Liquid edit will fix it.
- Where am I trying to render it? Product template, blog template, Custom Liquid block on a card, section, header. Each context has different variables in scope. A block on a card needs
closest.product, notproduct. - What's the metafield type? A list type needs a for-loop. A single reference needs
.valueand then field access. An image needs| image_urlor| file_url. The output shape follows the type. - For metaobject references, do I know the field keys exactly? Not the labels - the keys. They're in Settings, Custom data, Metaobject definitions, in the Key column. Guessing them produces empty output with nothing logged anywhere.
- For shop-level metafields, is "Storefronts" access enabled on the definition? If not, Liquid on your storefront cannot read the field at all, and the lookup returns nothing.
Most shopify metafields liquid issues I see find their fix in one of these five items within a few minutes of checking. If none of them does, the problem is usually a Horizon-specific gap or a metaobject-handle hyphenation oddity, and at that point I want a second pair of eyes anyway. If you've worked through these and you're still stuck, the first 15 minutes with a help1 expert is free and worth using on it.
What I've learned to assume
I used to assume the metafield itself was broken when output came back blank. After three years of running these audits, I assume the opposite. The metafield is almost always working. Something between the metafield and the page is filtering its output to nothing - a missing .value, a variable from the wrong scope, an access setting that was never flipped, or a definition with no actual data behind it.
I have not done this debugging walk on OS1 themes. Brooklyn, Minimal, and Debut predate the OS2.0 metafield-access patterns I described above, and the syntax behaves differently there. If you're on an OS1 theme, half this advice may not apply, and the right move is probably a theme migration before you spend more time patching Liquid the theme architecture wasn't built for.
One question worth sitting with
When a shopify metafields liquid call comes back blank, the instinct is to rewrite the Liquid. Open the file, swap the syntax, try a different filter, see what happens. Sometimes that helps. Most of the time, the file you're editing isn't where the problem is.
So here's the question worth answering before you change a single character: when did you last open the metafield definition itself in admin and confirm that the data is actually entered, the storefront access is actually on, and the field keys you're referencing are spelled exactly the way you've written them in your Liquid? If you can't answer that without going to look, the next thing to do isn't in your Liquid file. It's in Settings, Custom data.
Still stuck? Talk to an expert.
Our vetted Shopify experts can fix this issue for you in a live session. $39 per session. Your first 15 minutes are free.