Prisma Database Fix 8 min read

Prisma Connection Error? Fix P1001, P1017 & Pool Exhaustion

Troubleshoot Prisma database connection errors — P1001 can’t reach server, P1017 connection closed, P1003 database not found, connection pool exhaustion on Neon and Supabase, and migration command differences.

Supabase live status

Supabase — live status

Updated every 5 minutes · Full incident history →

Full status →

Common errors and fixes

P1001: Can’t reach database server at host:port

Prisma cannot open a TCP connection to the database. The most common cause is a wrong DATABASE_URL or a firewall blocking the connection.

Error: P1001: Can’t reach database server at `db.example.com`:`5432`
# Check connectivity from your app's environment
npx prisma db pull

# Verify the URL format is correct:
# PostgreSQL
DATABASE_URL="postgresql://USER:PASSWORD@HOST:5432/DBNAME?schema=public"
# MySQL
DATABASE_URL="mysql://USER:PASSWORD@HOST:3306/DBNAME"

# For Neon — use the POOLED string (not direct)
DATABASE_URL="postgresql://USER:[email protected]/neondb?sslmode=require"

# For Supabase — use the pooler URL (port 6543, not 5432)
DATABASE_URL="postgresql://postgres.REF:[email protected]:6543/postgres"
  • VPC / private networking: if your database is inside a VPC (AWS RDS, GCP Cloud SQL), your app must be in the same VPC or connected via a tunnel. Vercel and Netlify functions are on the public internet — you need VPC peering or a proxy like Prisma Accelerate.
  • SSL required: most cloud databases require ?sslmode=require appended to the URL. Without it, connections are rejected.
  • Special characters in password: URL-encode any special characters in your password (e.g. @ becomes %40).

P1017: Server has closed the connection (pool exhaustion)

In serverless environments, each function invocation that creates a new PrismaClient() opens a fresh connection pool. With concurrent invocations, you exhaust the database’s connection limit within seconds.

Error: P1017: Server has closed the connection.

Fix 1 — PrismaClient singleton (prevents new pools on hot-reload):

// lib/prisma.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined;
};

export const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
  });

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;

Fix 2 — Use a connection pooler (Neon Supavisor / PgBouncer):

# .env
# Pooled URL for runtime queries (?connection_limit=1 = one connection per serverless fn)
DATABASE_URL="postgresql://USER:PASSWORD@HOST:6543/DBNAME?pgbouncer=true&connection_limit=1"

# Direct URL needed for migrations (schema changes bypass the pooler)
DIRECT_URL="postgresql://USER:PASSWORD@HOST:5432/DBNAME"
// schema.prisma
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")   // pooled — used at runtime
  directUrl = env("DIRECT_URL")     // direct — used for migrate/introspect
}
  • Neon: use the pooled connection string (Supavisor, port 5432 with ?pgbouncer=true) from the Neon dashboard. The direct connection string is for local dev and migrations only.
  • Supabase: port 6543 = PgBouncer (transaction mode, use for serverless); port 5432 = direct (use only for migrations via DIRECT_URL).

P1003: Database does not exist

The database name in your DATABASE_URL does not exist on the server. Common after copy-pasting a connection string with the wrong database name, or forgetting to create the database.

Error: P1003: Database `mydb.public` does not exist on the database server at `localhost:5432`.
# Create the database (PostgreSQL)
psql -U postgres -c "CREATE DATABASE mydb;"

# Or use Prisma to create it and apply migrations in one step
npx prisma migrate dev --name init

# Verify database name in your URL (the path segment after the last /)
# postgresql://user:pass@host:5432/THE_DB_NAME_HERE
  • Neon default database: the default database name is neondb, not postgres — check your Neon dashboard under Connection Details.
  • Supabase default database: always postgres. Do not change it unless you created a secondary database.

prisma migrate deploy vs prisma db push

Mixing these up is one of the most common Prisma mistakes in CI/CD pipelines.

Never run prisma migrate dev in production. It requires a shadow database (a second temporary database Prisma creates and drops), and it is designed for local development only.
# LOCAL DEVELOPMENT
npx prisma migrate dev --name add_user_table
# Generates migration SQL, applies it, regenerates Prisma Client
# Requires DIRECT_URL (not pooled) for the shadow database

# PRODUCTION / CI
npx prisma migrate deploy
# Applies pending migrations from prisma/migrations/ in order
# Safe, idempotent, no shadow database required

# PROTOTYPING ONLY (destroys migration history)
npx prisma db push
# Directly syncs schema.prisma to the DB — no migration files created
# Will drop columns if you remove them from schema without a migration

Typical CI/CD deploy step:

# package.json
{
  "scripts": {
    "build": "prisma generate && next build",
    "postinstall": "prisma generate",
    "db:migrate": "prisma migrate deploy"
  }
}

# Run before starting the app in production:
# npm run db:migrate && npm start

P2002: Unique constraint violation

A create or update would duplicate a unique field (e.g., email already exists). Handle it gracefully:

Error: P2002: Unique constraint failed on the fields: (`email`)
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
import { prisma } from '@/lib/prisma';

async function createUser(email: string, name: string) {
  try {
    return await prisma.user.create({
      data: { email, name },
    });
  } catch (e) {
    if (e instanceof PrismaClientKnownRequestError && e.code === 'P2002') {
      const field = (e.meta?.target as string[])?.join(', ') ?? 'unknown field';
      throw new Error(`A user with this ${field} already exists.`);
    }
    throw e;
  }
}

// Or use upsert to avoid the race condition entirely:
await prisma.user.upsert({
  where: { email },
  update: { name },
  create: { email, name },
});

Get an email the next time Supabase goes down

Outage alerts for Supabase, straight to your inbox. No account needed, unsubscribe in every email.

Watching more than one service? A free account covers 5 services + a daily digest — and Pro is currently free.

FAQ

What causes Prisma P1001 “Can’t reach database server” error?

P1001 means Prisma cannot open a TCP connection to your database. Most common causes: wrong host or port in DATABASE_URL; database in a private VPC your app cannot reach; using a direct connection string on a provider that requires a pooled one (Neon, PlanetScale); or a firewall blocking port 5432. Run npx prisma db pull to test connectivity from your environment.

What causes Prisma P1017 “Server has closed the connection”?

P1017 is almost always connection pool exhaustion. In serverless environments (Vercel, Netlify, Cloudflare Workers), each function invocation that calls new PrismaClient() opens new connections. Fix: use a PrismaClient singleton and switch to a pooled connection string with ?connection_limit=1&pgbouncer=true.

What is the difference between prisma migrate deploy and prisma db push?

prisma db push syncs your schema directly to the database without migration files — fast but destructive, for prototyping only. prisma migrate deploy applies SQL migration files in order and is safe for production. Never run prisma migrate dev in production — it requires a shadow database.

How do I fix Prisma P2002 unique constraint violation?

Catch PrismaClientKnownRequestError and check error.code === 'P2002'. Inspect error.meta.target to see which field caused the violation, then return a user-friendly message or use upsert instead of create.

Monitor related services