Stripe says "Neither apiKey nor config.authenticator provided". What does it mean?

Checked on

the Stripe server library was created with an empty or undefined secret key. Usually process.env.STRIPE_SECRET_KEY (or Deno.env.get('STRIPE_SECRET_KEY')) isn't set where the code runs: missing from the Production environment, misspelled, set after the last deploy, or read at build time when it isn't available. Set the key for the right environment, redeploy, and create the Stripe client inside your handler instead of at the top of the file.

Symptoms

  • Checkout button does nothing or shows an error; server logs show Error: Neither apiKey nor config.authenticator provided.
  • next build fails 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 stripe npm 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:

  1. Not set for Production. On Vercel, each variable is applied to Production, Preview and/or Development separately (Vercel: Environment variables).
  2. 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).
  3. Name mismatch. STRIPE_SECRET_KEY vs STRIPE_SECRET vs STRIPE_API_KEY.
  4. 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 during next build, when your secret may not be available.
  5. 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 to NEXT_PUBLIC_ or VITE_, because that publishes it.
  6. 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_KEY with 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:

Fix prompt
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

  1. 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.
  2. Check the server or function logs: no Neither apiKey error.
  3. In live mode, confirm the key in Production starts with sk_live_ or rk_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.

More questions

It works locally. Why not in production?

Your local .env has the key, and the host doesn't (or didn't when it last built).

I set the variable on Vercel but it still fails.

Redeploy. Old deployments keep the old environment.

Can I put the secret key in VITE_STRIPE_SECRET_KEY to make it available?

No. VITE_ and NEXT_PUBLIC_ values end up in the JavaScript every visitor downloads (Vite: Env variables). Stripe: "Only publishable keys are safe to expose outside your application's backend" (Stripe: API keys).

Should I downgrade stripe below v17?

No. That would hide the missing key rather than fix it.

Where this comes from