Symptoms
- Checkout button does nothing or shows an error; server logs show
Error: Neither apiKey nor config.authenticator provided. next buildfails on Vercel with this error while collecting page data.- It started right after moving from test keys to live keys (one v0 user's report: Vercel Community).
- It started after upgrading the
stripenpm package, even though nothing else changed.
Why it happens
The message comes from Stripe's Node library (stripe-node). In the library's GitHub issue, a contributor explains that the only way they could reproduce it was "if my environment variable for key was null," and that "Prior to v17, passing a null key into the Stripe constructor would succeed but would create a Stripe object with invalid auth. After v17, it raises this error" (stripe-node#2207). So an upgrade can surface a key problem that was always there.
Common reasons the key is empty:
- Not set for Production. On Vercel, each variable is applied to Production, Preview and/or Development separately (Vercel: Environment variables).
- Set after the last deploy. "Any change you make to environment variables are not applied to previous deployments, they only apply to new deployments" (Vercel).
- Name mismatch.
STRIPE_SECRET_KEYvsSTRIPE_SECRETvsSTRIPE_API_KEY. - Created at module load during the build.
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)at the top of a file runs when Next.js imports the module, including duringnext build, when your secret may not be available. - Read in browser code. Server-only variables aren't available in the browser. In Next.js, only
NEXT_PUBLIC_values are inlined into client code (Next.js: Environment variables). Never "fix" this by renaming the secret key toNEXT_PUBLIC_orVITE_, because that publishes it. - Supabase Edge Function secret missing. The function reads
Deno.env.get('STRIPE_SECRET_KEY'), but the secret was never set on the project.
Step-by-step fix
1. Find where Stripe is created
Search the code for new Stripe(. Note which environment variable it reads and where the file runs (Next.js route, server action, Edge Function).
2. Set the key in the right place
- Vercel / v0: add
STRIPE_SECRET_KEYwith the Production environment selected (and Preview if you test there), then trigger a new deployment. v0 apps inherit environment variables from the connected Vercel project (v0: Vercel integration). - Supabase Edge Functions: set it under Edge Function Secrets in the dashboard or with
supabase secrets set STRIPE_SECRET_KEY=sk_.... Supabase says functions "read a new secret immediately, so you don't need to redeploy" (Supabase: Function secrets). - Lovable: Lovable prompts you to add keys such as Stripe's securely when they're needed (Lovable: Cloud). If the key was skipped, ask Lovable in chat to add the Stripe secret key again.
3. Use the right key for the mode
Live mode needs a live key (sk_live_..., or a restricted rk_live_...); test mode uses sk_test_.... Stripe: "Each mode has its own set of API keys, and objects in one mode aren't accessible to the other" (Stripe: API keys). Stripe now recommends restricted keys (RAKs) for most uses.
4. Create the client lazily, and fail loudly
// lib/stripe.ts
import Stripe from 'stripe'
let client: Stripe | null = null
export function getStripe(): Stripe {
const key = process.env.STRIPE_SECRET_KEY
if (!key) throw new Error('STRIPE_SECRET_KEY is not set in this environment')
if (!client) client = new Stripe(key)
return client
}// app/api/checkout/route.ts
import { getStripe } from '@/lib/stripe'
export async function POST(req: Request) {
const stripe = getStripe() // created at request time, not at build time
// ... create the Checkout Session
}Now a missing key gives a clear message in your logs, and the build no longer needs the secret.
5. Redeploy and retest
Redeploy (Vercel/Netlify) or republish (Lovable/Bolt), then try checkout again.
Fix prompt for your AI builder
Paste this into Lovable, Bolt, Cursor, Replit, v0 or whichever tool you build with:
Stripe fails with "Neither apiKey nor config.authenticator provided". Fix only how the Stripe client gets its key:
1. Find every `new Stripe(` call. Replace module-level clients with a getStripe() helper that reads the secret key from server-side env (process.env.STRIPE_SECRET_KEY, or Deno.env.get('STRIPE_SECRET_KEY') in Supabase Edge Functions) at request time and throws a clear error if it's missing.
2. Make sure the secret key is only ever read in server code. Never use a VITE_ or NEXT_PUBLIC_ variable for it.
3. Tell me the exact env var name the code expects and where I need to set it (host Production environment or Supabase Edge Function secrets).
Don't change prices, products, webhook logic or UI.How to confirm it's fixed
- After redeploying, open your live pricing page in a private window and click through to checkout. Stop when the Stripe payment page appears; you don't need to pay.
- Check the server or function logs: no
Neither apiKeyerror. - In live mode, confirm the key in Production starts with
sk_live_orrk_live_.
How to find out sooner next time
Heedza checks on a schedule that your checkout page opens and shows its pay form or pay button. It stops there and never types a card or clicks Pay. If a missing key breaks checkout before that point, you get an alert with a screenshot and a fix prompt. One limit: if your checkout page is only created after a button click (common with Stripe Checkout), Heedza may only reach your pricing page. And if a Stripe secret key has slipped into frontend code, the free scan flags it.
Let Heedza check this for you. Heedza’s payment page check opens your live payment page on a schedule and tells you if the Pay button disappears. It never pays. It never logs in or fills in forms, so it can’t see errors that only show after that.
Made with Lovable, Bolt, Replit, Cursor or v0? Free for 1 site: start free or see prices.