stripe "testmode_charges_only" after a key rotation: livemode key missing
Explains the Stripe testmode_charges_only error after rotating API keys: a test-mode secret key is being used where live charges should happen. Use when live payments suddenly fail with this error post-rotation. Not for actual test-mode integration work.
TL;DR
Your app is sending live payment requests with a test-mode key. Go back to wherever you deployed the rotated key and replace it with the live secret key from the Stripe dashboard, then redeploy. The fix is a config value, not code.
testmode_charges_onlyUse this when
- Live charges fail with testmodechargesonly right after a key rotation.
- Staging and production configs may have crossed during the rotation.
- You need a checklist to verify key mode before the next rotation.
Not for this skill when
- You are deliberately working in test mode. testmodechargesonly is expected there when live methods are attempted.
- The error is authenticationfailed or invalidapi_key. That is a wrong key, not a wrong-mode key.
Steps
- ('Confirm the failing requests use a key starting with sktest while targeting live resources. Verify: the key prefix in your environment config.', 'In the Stripe dashboard, switch to live mode and copy the correct live secret key. Verify: the prefix is sklive.', 'Replace the key in every place it lives: env vars, secrets manager, CI config, per-worker configs. Verify: grep your configs for sktest in production paths.', 'Redeploy or restart the services that read the key at boot. Verify: a live-mode test request succeeds without the error.', 'Write the mode into the rotation checklist: every future rotation verifies sklive vs sk_test in each environment before rollout.')
Variant phrasings
stripe testmodechargesonly error
The verbatim search.
stripe live payments failing after api key rotation
Incident phrasing.
sk_test in production stripe
The config-smell search.
Compatibility: Stripe API, all client versions. Secret key mode is determined by the sklive vs sktest prefix.
Why it happens
Stripe keys are mode-scoped: a test key can only touch test data, and any attempt to create a real charge with one is rejected with testmodechargesonly. Key rotations fail this way when someone copies the test key into production config, often because the dashboard defaults to test mode and the copy button grabs whatever mode is active. The API rejects the request by design rather than silently creating a test charge.
Edge cases / pitfalls
- Restricted keys also carry mode. A live-mode restricted key is still live; do not confuse this error with permission errors.
- Publishable keys have the same split (pktest vs pklive). A live page loading pk_test breaks Elements.
- Search every service for the key, not just the billing service. Cron workers and webhooks often hold their own copy.
- Store keys in a secrets manager with environment scoping so test and live can never cross again.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_J2TzlD0yKv2BR0fA3HIhbw