Vercel CI Hell: Fix Next.js Tests Failing in Production
Tests pass locally but fail on Vercel? The real fix is one line: NODE_ENV in vitest.config.ts, plus CI-aware timeouts. My 4 wasted hours, condensed. →
Óscar Gallego
Web Developer
On this page
Why do my tests pass locally but fail on Vercel?
When tests work on your machine but die in Vercel’s CI, it’s usually two environment differences: an incorrect NODE_ENV configuration, and timeouts tuned for hardware 5-10x faster than the CI container.
The fast checklist:
- Force
NODE_ENVto'test'. Yourvitest.config.tsneedsenv: { NODE_ENV: 'test' }. Vercel might default to production mode, which strips out React’s testing utilities. - Raise timeouts for CI. Vercel’s CI is 5-10x slower than a local machine. Make timeouts environment-aware:
const TIMEOUT = process.env.CI ? 10000 : 5000;. - Read the Vercel logs. Look for the specific tests that fail or crawl. The
actImplementation is not a functionerror is often a symptom of theNODE_ENVissue. - Mock heavy operations. No file I/O, no giant test data. Lightweight mocks instead.
- Don’t blame the libraries yet. Before assuming version compatibility problems, check your environment configuration. It’s the most common cause.
The rest of this post is how I learned each of those the slow way.
Same code, same dependencies, different results
# Local
$ pnpm test
✓ All tests pass (181 passed)
# Vercel
❌ Build failed
Error: actImplementation is not a function
Stack: Next.js 16, React 19, Vitest, Vercel Time wasted: 4 hours chasing the wrong problem Root cause: misunderstanding Vercel’s CI environment
What I thought it was (the embarrassing part)
“React 19 compatibility issue with Testing Library!”
So I built, in order:
- a 125-line patch script for React internals
- a custom Vite plugin for module resolution
- mock files and import redirects
All of it failed on Vercel. All of it unnecessary. Four hours of confident engineering, aimed at a problem that didn’t exist.
The real problem
Your machine is not Vercel’s CI
Local development:
- Fast SSD, direct I/O
- Dedicated CPU/memory
- React loads in development mode
- Consistent environment
Vercel CI:
- Networked storage (slower I/O)
- Shared container resources
- May default to production mode
- Cold starts every build
The result: operations take 5-10x longer on Vercel.
The two actual issues
Issue 1: the environment mode
Vercel wasn’t loading React in test mode by default.
The fix:
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
// ← Forces React test mode
env: {
NODE_ENV: 'test',
},
},
})
Why this matters: React’s development build includes test utilities, and the production build strips them out. Without NODE_ENV: 'test', React loads incorrectly on Vercel.
Issue 2: resource constraints
CI containers are slower. Operations that feel instant locally time out on Vercel.
The fix:
// CI-aware timeout configuration
const TIMEOUT = process.env.CI ? 10000 : 5000
it('resource-intensive test', async () => {
// test code
}, TIMEOUT)
A real measurement from this debugging session:
// Creating a 100MB test file
const largeFile = createMockImageFile(
'large.jpg',
100 * 1024 * 1024,
'image/jpeg'
)
// Local: ~500ms
// Vercel CI: ~6-7 seconds (!)
The minimal configuration that works
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
globals: true,
css: true,
// Critical for Vercel CI
env: {
NODE_ENV: 'test',
},
// Global timeout with CI awareness
testTimeout: process.env.CI ? 10000 : 5000,
},
})
Environment-aware test timeouts
// test-config.ts
export const TEST_TIMEOUTS = {
FAST: process.env.CI ? 5000 : 2000,
MEDIUM: process.env.CI ? 10000 : 5000,
SLOW: process.env.CI ? 30000 : 15000,
}
// In your tests
import { TEST_TIMEOUTS } from './test-config'
describe('Feature', () => {
it('fast operation', async () => {
// Quick unit test
}, TEST_TIMEOUTS.FAST)
it('file I/O operation', async () => {
// File processing, small data
}, TEST_TIMEOUTS.MEDIUM)
it('complex operation', async () => {
// Large files, network, heavy processing
}, TEST_TIMEOUTS.SLOW)
})
The diagnostic checklist for next time
When tests fail on Vercel but pass locally:
- Check that
NODE_ENV: 'test'is set invitest.config - Read the Vercel build logs for slow tests
- Add explicit timeouts to resource-intensive tests
- Use environment-aware configuration
- Mock expensive operations (file I/O, large data)
- Do not start by assuming library incompatibility
Common patterns
// ❌ Assumes local environment
it('test', async () => {
await heavyOperation()
})
// ✅ Accounts for CI differences
it('test', async () => {
await heavyOperation()
}, process.env.CI ? 15000 : 5000)
// ❌ Creating real 100MB files
const largeFile = createRealFile(100 * 1024 * 1024)
// ✅ Use lightweight mocks
const largeFile = createMockFile({ size: 100 * 1024 * 1024 })
What four wasted hours taught me
1. Vercel CI is not your MacBook
Design tests with CI in mind from the start: containerized builds, shared resources, slower I/O, different defaults.
2. Configure explicitly
Don’t rely on default behavior. Set NODE_ENV explicitly, set timeouts for slow operations, make configs environment-aware.
3. Error messages can mislead
actImplementation is not a function looked like a React compatibility problem. The actual cause: React loading in the wrong mode due to environment misconfiguration.
4. Design for CI from the start
Write tests knowing they’ll run on slower infrastructure. Account for cold container starts, networked storage, resource throttling, and shared CPU and memory.
What makes Vercel’s build environment different
Containerized builds. Each build runs in an isolated container. Containers share infrastructure resources, and I/O is networked, not local.
Cold starts. Containers spin up fresh for each build. No warm filesystem cache, dependencies downloaded every time.
Resource limits. CPU/memory throttling, shared infrastructure, different performance characteristics.
Environment variables. Different defaults than your local .env. NODE_ENV might not be what you expect, so configure it explicitly.
Configure for it. Don’t fight it.
Vercel’s CI is slower than your machine (5-10x), runs Node.js differently, and needs explicit configuration to behave.
The error message pointed at React. The problem was Vercel’s environment. Knowing the difference is the whole fix, and it’s a one-line config change once you stop patching React internals.
Sources
Related reading: another afternoon lost to a tool silently ignoring my config: the misplaced content.config.ts that ate my Astro schema fields.
P.S. Fought your own round with Vercel’s CI? Tell me what the error message claimed and what the problem actually was, on Twitter/X.


