Fixing Shopify Flow JSON: Gracefully Handling Empty Metafields in Liquid

Hey there, fellow store owners and Shopify enthusiasts!

I recently dove into a really valuable discussion on the Shopify community forums that touched on a super common pain point: handling blank or unset metafields when you’re trying to format JSON payloads in Liquid. If you’ve ever tried to send data to an external service like Klaviyo using Shopify Flow, only to find your JSON breaking because a metafield wasn’t filled in, you know exactly what I’m talking about. It's frustrating, right?

The original question came from @ctevan, who was trying to include a non-mandatory "Eco advisor" metafield from Draft Orders into a Klaviyo event. The challenge was, if this metafield was left empty, the JSON payload would just… break. They wanted it to send "none" instead of nothing, but their initial attempts weren't quite hitting the mark. This is a classic scenario, and thankfully, the community jumped in with some fantastic insights.

Understanding Why Blank Metafields Break Your JSON

Before we jump into the solutions, let's quickly understand the root of the problem. When you’re building a JSON string in Liquid, every part needs to be valid. If you have something like "key": with nothing after the colon, or an unquoted value where a string is expected, your JSON parser will throw an error.

One of the trickier aspects, as @One-Sun1995 pointed out, is how Liquid treats list metafields. Even if a list metafield is "unset" or has no items, it often comes back as an empty array. And here's the kicker: an empty array in Liquid is considered "truthy." So, an {% if metafield.value %} check would pass, but then your loop would have nothing to iterate over, potentially leaving a gap in your JSON. Plus, a bare none (without quotes) will also invalidate your JSON, as @IndustryInsightsDesk highlighted.

The Community's Best Solutions: Robust Metafield Checks & The JSON Filter

The thread brought out some really solid advice, and two main themes emerged: using the powerful | json filter and implementing robust checks for truly blank or empty metafields. Let's break down the best practices:

1. Embrace the | json Filter

This was a unanimous recommendation from @IndustryInsightsDesk and @Josh-FiveAcreCode, and it's a game-changer. Instead of manually adding quotes around your Liquid variables (e.g., "{{ variable }}"), use the | json filter. Why? Because it automatically adds the necessary quotes and, critically, escapes any special characters within your value (like double quotes or backslashes). This ensures your output is always valid JSON, no matter what crazy text your metafield holds.

Wrong: "key": "{{ my_value }}" (if my_value contains " or \, it breaks)
Right: "key": {{ my_value | json }} (Liquid handles all the escaping for you!)

2. Smarter Checks for Blank or Empty Metafields

This is where the nuance comes in, especially for list-type metafields:

  • For single-line text, number, or URL metafields: The simplest and most reliable check is {% if my_metafield_value != blank %}. As @Josh-FiveAcreCode demonstrated, assigning the .value to a variable first makes your code cleaner and easier to read.
  • For list metafields (like "list of text" or "list of numbers"): This is where @One-Sun1995's advice shines. Since an empty list is "truthy," you need to check its size. So, {% if eco and eco.size > 0 %} is the way to go. The eco check ensures the metafield itself exists (was set at all), and eco.size > 0 ensures it actually contains items.
  • Alternatively for lists, @IndustryInsightsDesk suggested capturing the concatenated list into a string first and then checking if that string is != blank. This works great if you want to join the list items into a single string for your JSON output.

3. Deciding on Your Fallback: "none", null, or Omission?

Once you've determined a metafield is blank, you have a few options for your JSON output:

  • A specific string (e.g., "none"): If you want to explicitly state "none" (or "N/A", "Unknown", etc.), make sure it's properly quoted. So, ""none"" or, even better, "none" | json.
  • A JSON null value: This is often the most semantically correct choice for truly empty data in JSON. As @Josh-FiveAcreCode showed, you simply output null (without quotes). Liquid will recognize this as the JSON null type.
  • Omit the field entirely: Sometimes, if a value is missing, you might not want the field to appear in your JSON at all. This keeps your payload cleaner. @Josh-FiveAcreCode provided an excellent example of how to conditionally include the field and its preceding comma.

Step-by-Step: Fixing Your Draft Order Event JSON

Let's apply these insights to @ctevan's original problem with the "Eco advisor" list metafield. We'll look at a few common scenarios:

Option 1: Fallback to a Specific String ("none")

This approach combines @One-Sun1995's robust list check with the | json filter for safety. This is ideal if you need a specific string placeholder.

  1. Assign the metafield value: Always a good first step for clarity.
  2. Check for existence and size: Use {% if eco and eco.size > 0 %}.
  3. Join and JSON-filter the value: If it exists, join the list items into a string (e.g., join: ', ') and then apply | json.
  4. Provide a quoted fallback: In the else branch, output your fallback string, also passed through | json for good measure, or simply ""none"" if you are absolutely sure it doesn't need escaping.

Here’s how it looks:

{% assign eco = getDraftOrderDataForeachitem.ecoAdvisor.value %}
"eco_advisor": {% if eco and eco.size > 0 %}{{ eco | join: ', ' | json }}{% else %}"none" | json %}{% endif %},

Or, if you prefer the capture method for lists (as suggested by IndustryInsightsDesk for Flow context):

{% capture advisor_string %}{% for value_item in getDraftOrderDataForeachitem.ecoAdvisor.value %}{{ value_item }}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endcapture %}
"eco_advisor": {% if advisor_string != blank %}{{ advisor_string | strip | json }}{% else %}"none" | json %}{% endif %},

I added {% unless forloop.last %}, {% endunless %} inside the capture to get a comma-separated list, which is usually more useful than just concatenating. Then strip removes any leading/trailing whitespace from the captured string before JSON filtering.

Option 2: Fallback to JSON null

If you prefer a true JSON null when the metafield is empty, this is a clean way to do it. This is often the preferred method for API integrations.

  1. Assign the metafield value: Same as above.
  2. Check for existence and size: Use {% if eco and eco.size > 0 %}.
  3. Join and JSON-filter the value: If it exists, join the list items and apply | json.
  4. Output null: In the else branch, simply output null.

The code would look like this:

{% assign eco = getDraftOrderDataForeachitem.ecoAdvisor.value %}
"eco_advisor": {% if eco and eco.size > 0 %}{{ eco | join: ', ' | json }}{% else %}null{% endif %},

Option 3: Omit the Field Entirely if Blank

Sometimes, less is more. If an empty metafield means the data isn't relevant for your event, you can omit the field entirely. This requires a bit more care with your JSON commas.

  1. Assign the metafield value: Still a good practice.
  2. Check for existence and size: Use {% if eco and eco.size > 0 %}.
  3. Conditionally include: Wrap the entire key-value pair, including the preceding comma, within your if statement.

Here's a general example (adapting @Josh-FiveAcreCode's structure for a list):

{% assign eco = getDraftOrderDataForeachitem.ecoAdvisor.value %}
{
  "order_id": {{ getDraftOrderDataForeachitem.id | json }}
  {% if eco and eco.size > 0 %},
  "eco_advisor": {{ eco | join: ', ' | json }}
  {% endif %}
}

Notice how the comma before "eco_advisor" is inside the if block. This ensures that if eco_advisor is omitted, you don't end up with a trailing comma after "order_id", which would also break your JSON.

So, there you have it! This discussion really highlighted how crucial it is to be precise when dealing with dynamic data in Liquid, especially when it's destined for JSON. The `| json` filter is your best friend for escaping, and understanding how to properly check for empty lists versus single blank values will save you a lot of headaches. A big shout-out to @One-Sun1995, @IndustryInsightsDesk, and @Josh-FiveAcreCode for their excellent contributions to the thread. Applying these techniques will make your Shopify Flow automations and external event tracking much more robust and reliable.

Share:

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools