Troubleshooting
Fix the most common Joy Loyalty issues on Hydrogen and Weaverse storefronts — token errors, Content Security Policy blocks, caching and empty blocks.
Common issues
| What you see | Cause | Fix |
|---|---|---|
/api/joy-token returns 500 missing_env | A Joy environment variable is missing. | Add JOY_API_URL, JOY_SHOP_DOMAIN and JOY_API_SECRET to .env and to each Oxygen environment (Preview and Production are separate). |
Logged-in customers get 401 exchange_failed, or the token request repeats | JOY_API_SECRET is wrong, was revoked, or is the REST Secret key instead of the Headless secret — or JOY_SHOP_DOMAIN isn't your myshopify domain. | Copy the current secret from Joy admin > Settings > Manage keys > Headless secret, and use your-store.myshopify.com. Check the secret first. |
Logged-in customers get 401 customer_unresolved | Hydrogen couldn't read the customer's ID or email from the Customer Account API. | Check your Customer Account API login setup. |
/api/joy-token returns 502 exchange_failed; Joy API requests in the network tab return 403 with PLAN_NOT_ELIGIBLE | Your store isn't on the Ultimate plan. | Upgrade to the Ultimate plan in Joy admin. |
Joy API requests return 429 with RATE_LIMITED (or /api/joy-token returns 502 exchange_failed under load) | Too many requests in a short time. | Wait and retry. Make sure your code doesn't call /api/joy-token or the Joy API in a loop. |
One customer gets 502 exchange_failed while others work | The customer is excluded from your loyalty program (EXCLUDED). | Expected — excluded customers can't sign in to Joy. Remove the exclusion in Joy admin if this is a mistake. |
/api/joy-token returns 502 upstream_unreachable | JOY_API_URL is wrong or unreachable. | Use https://api.joy.so/app/api/v1/headless, including the path. |
| Widget stuck on a loading skeleton, or "[joy] failed to load the widget core" in the console | The widget code is blocked by script-src, JOY_WIDGET_ASSET_URL points somewhere that doesn't respond, or you are on @joy-loyalty/hydrogen 0.2.0 without JOY_WIDGET_ASSET_URL (its default static.joy.so doesn't serve the widget). | Merge the Joy CSP directives. Upgrade to @joy-loyalty/hydrogen 0.2.1 or later and unset JOY_WIDGET_ASSET_URL — or, on 0.2.0, set JOY_WIDGET_ASSET_URL=https://api.joy.so. |
Images or icons missing; CSP errors for img-src or font-src | Only some Joy CSP lists were merged. | Merge all five joyCspDirectives lists and keep cdn.shopify.com in imgSrc. |
| Hydrogen analytics or consent error | The checkout domain isn't set. | Set PUBLIC_CHECKOUT_DOMAIN, or fall back to your store domain. |
Build error: cannot resolve dompurify | Your package manager doesn't hoist dependencies (pnpm, Yarn PnP). | Run npm i dompurify (or your package manager's equivalent). |
| Logged-in customer sees the guest widget | A cached guest page was served. | Follow the full-page cache rules in Deploy and verify. |
| Joy texts in the wrong language | locale isn't passed, or isn't lowercase. | Pass storefront.i18n.language.toLowerCase(). |
| Redeemed coupon shows a code but isn't applied | Cart wiring is incomplete. | Pass both cartFetcher and cartRevalidate, and a locale-aware cartRoute. See Loyalty widget. |
| Two launchers on the page | Both <JoyWidget/> and the Weaverse launcher block are used. | Keep one. See Weaverse blocks. |
| Loyalty Page or Weaverse block is empty | The store is on Loyalty Page V1, or Joy variables are missing. | Switch to Loyalty Page V2 in Joy admin and check the environment variables. |
| Old Weaverse Joy blocks disappeared after updating | Version 0.2.0 replaced them with the Loyalty Page block. | Add the Joy — Loyalty Page block and pick the section you need. |
Using an AI coding agent? Ask it: "Use the joy-headless-install skill to diagnose my Joy Loyalty integration. Symptom: <what you see>." See Install with an AI agent.
Still stuck?
Contact us through the in-app chat with your storefront URL, the symptom, and a screenshot of the browser console and network tab — we are happy to help.