DEV Community

Eli
Eli

Posted on • Originally published at clawmama.run

Your Shopify Discount Function Can Silently Stop Working: The 10,000-Byte Metafield Trap

You built the right architecture: business rules in a JSON metafield, a Shopify Discount Function that reads them, no redeploys when a promotion changes. It works in testing. It works for months. Then the merchant adds one pricing tier too many — and every discount on the store silently stops applying. No error at checkout. Nothing in the order. The Function runs fine; it just returns nothing.

This failure mode costs money precisely because nothing looks broken. Here's why it happens and the design that survives it.

(Full bilingual writeup: canonical post on the ClawMama blog. This is part 2 of a Shopify-Functions-in-production series — part 1: rejecting discount codes cleanly. We build open-source Shopify agent skills — github.com/clawmama-run/shopify-growth-operator-agent — and a ready-to-use Shopify Agent.)

The two limits behind it

Both are documented on Shopify's Functions API page, buried in a limits table most people read once during setup.

Limit 1: the input query is static and capped at 3,000 bytes. Your Function's input query is fixed at build time (run.graphql). You can't add a field at runtime every time the merchant invents a new rule type. That's exactly why the config-as-JSON-blob pattern exists: one metafield field in the query, arbitrary structure inside.

Limit 2: a metafield value over 10,000 bytes is not returned. From the docs: "Metafields with values exceeding 10,000 bytes in size will not be returned." Not truncated — absent. The metafield node comes back empty, as if never set.

Follow the failure through your Function:

  1. The config JSON grows past 10,000 bytes (a few dozen pricing tiers with conditions gets there fast).
  2. The input query asks for the metafield; Shopify withholds it.
  3. Your Function sees "no config" and does its unconfigured behavior — parse nothing, match nothing, return empty operations.
  4. The store simply stops discounting. Checkout is valid. Orders complete. Nobody is notified.

And your last line of defense can't save you: Function logs are capped at 1 kB written, truncated.

The design that survives

Keep the config-in-metafield pattern (it's Shopify's own best practice for complex configuration) — but make it size-aware and fail-loud:

1. Budget the bytes, and measure them. 10,000 is the hard ceiling; treat ~8,000 as your alert line. The admin UI where merchants edit config should display the current byte size. new TextEncoder().encode(json).length — count bytes, not characters; CJK characters are 3 bytes each.

2. Fail loud, not silent. Decide what the Function does when the config metafield comes back absent, and make "absent" distinguishable from "legitimately empty." Apply a safe default and alert, or apply nothing but notify through your app's own monitoring (a daily Admin API job that reads the metafield — where it is returned — and checks size). The worst option is the default: shrug, return empty operations.

3. Shard before you hit the wall. Split config by domain — VIP tiers, quantity breaks, B2B rules — under your reserved $app: namespace, queried as separate fields. Three 4 KB metafields all come back; one 12 KB metafield comes back as nothing. Mind the query-side caps while sharding: list arguments max 100 elements, input query cost limit 30.

4. Test the boundary. Add a deliberately-over-10KB fixture config to your dev store test suite. Lock in "we get alerted," not "discounts vanish quietly."

Check your exposure today

  1. Via Admin GraphQL, fetch the config metafield and measure the byte length of value (Admin API returns full values regardless of size — the cutoff applies to Function input queries only).
  2. Under 8 KB: add the size display + alert, re-check as rule count grows.
  3. Over: your discounts may already be intermittent. Check recent orders for missing discount applications, then shard.

Where an agent fits

The design fix is one-time. The ongoing risk is drift: rule counts creep, someone pastes a giant JSON, a new promotion type doubles the config. That's a monitoring loop a store operations agent can own — watching config size and discounted-order rate, flagging anomalies to the owner with evidence attached, before a customer notices.


Verified against Shopify's Functions API documentation (input query limits, fixed limits) as of July 2026. Field report via a Shopify Community thread — the practitioner who documented this failure mode had the numbers exactly right.

Top comments (0)