Fix guides / Stripe: "No such price ... a similar object exists in test mode"
How to fix Stripe "No such price ... a similar object exists in test mode"
Your Stripe checkout worked perfectly while testing. You switch to your live secret key, click Buy, and get an error like No such price: 'price_1Q...'; a similar object exists in test mode, but a live mode key was used to make this request. Stripe is being literal: that price exists, but only in your test data, and live mode cannot see it.
Why this happens
Stripe keeps test mode and live mode completely separate. Products, prices, customers, coupons, webhooks, and payment links you create in test mode do not exist in live mode, and they get different IDs when you recreate them. AI builders usually paste the test price_... IDs straight into your code or database while you are testing. When you later swap in sk_live_..., every one of those IDs breaks.
The same message shows up for other objects too: No such customer, No such coupon, No such promotion code, No such product, or No such checkout session. The cause is the same in every case.
Step 1: confirm which mode each piece is in
- Secret keys start with
sk_test_orsk_live_. Publishable keys start withpk_test_orpk_live_. Both must be the same mode. - In the Stripe Dashboard, the test mode toggle (or the sandbox switcher) decides which data you are looking at. Turn test mode off before copying any ID you plan to use live.
- Price IDs do not show their mode in the ID itself, so the only reliable check is to open the price in the live Dashboard. If it is not there, it is a test price.
Step 2: recreate your products in live mode
- In the Dashboard, open a test mode product. Products can be copied to live mode with the Copy to live mode option on the product page, or you can create them again by hand.
- Turn test mode off, open each live product, and copy the new
price_...ID for every price.
Step 3: find every hard-coded ID
Search the whole project, including Supabase edge functions and any .env files, for IDs and keys:
grep -rn "price_\|prod_\|sk_test_\|pk_test_\|whsec_" --exclude-dir=node_modules .
Typical places Lovable, Bolt, and v0 apps keep them: a pricing page component, a plans config file, a create-checkout edge function, and a plans or products table in Supabase. Check the database too:
select * from plans; -- or products, prices, subscriptions
Step 4: stop hard-coding, read IDs from settings
Keep test and live values side by side by putting them in environment variables or secrets instead of code:
// Supabase edge function (Deno)
const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!, {
apiVersion: '2024-06-20',
});
const priceId = Deno.env.get('STRIPE_PRICE_PRO')!;
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${origin}/pricing`,
});
Set the live values with supabase secrets set STRIPE_SECRET_KEY=sk_live_... STRIPE_PRICE_PRO=price_... or in the dashboard under Edge Functions, then Secrets. Front-end keys on Vite need the VITE_ prefix and a rebuild before they change.
An alternative is lookup keys: give each price a stable name such as pro_monthly in both modes, then fetch it at runtime with stripe.prices.list({ lookup_keys: ['pro_monthly'] }). The code stays identical across modes.
Step 5: clear test customers from your database
If your app saved a stripe_customer_id for users while you were testing, live mode will reject those with No such customer. Either clear the column for test accounts so a new live customer is created on the next checkout, or handle it in code: when Stripe returns resource_missing for a saved customer, create a new one and save the new ID.
update profiles set stripe_customer_id = null where stripe_customer_id is not null; -- only if every saved ID is from test mode
Step 6: do not forget the webhook
Webhook endpoints are per mode as well. Create the endpoint again in live mode, pick the same events, and replace the STRIPE_WEBHOOK_SECRET with the new live whsec_... value. A test signing secret against live events fails with a signature verification error, and paid users never get upgraded.
Check the fix
- Run one real purchase on the cheapest price with your own card, then refund it in the Dashboard.
- In the live Dashboard, confirm the payment, the customer, and a successful webhook delivery all appear.
- Confirm the user's account shows the paid plan in your app.
Request a fix
Last updated 2026-10-19.