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:
- The config JSON grows past 10,000 bytes (a few dozen pricing tiers with conditions gets there fast).
- The input query asks for the metafield; Shopify withholds it.
- Your Function sees "no config" and does its unconfigured behavior — parse nothing, match nothing, return empty operations.
- 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
- 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). - Under 8 KB: add the size display + alert, re-check as rule count grows.
- 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)