Files
gh-neondatabase-labs-ai-rul…/skills/neon-drizzle/guides/troubleshooting.md
2025-11-30 08:43:11 +08:00

540 lines
11 KiB
Markdown

# Troubleshooting Guide
> **Reference Guide**: This is organized by error type and solution, not sequential phases.
> Jump directly to the error you're experiencing for quick resolution.
Common issues and solutions for Drizzle ORM with Neon.
## Table of Contents
- [Migration Errors](#migration-errors)
- [Connection Errors](#connection-errors)
- [Adapter Issues](#adapter-issues)
- [Type Errors](#type-errors)
- [Query Errors](#query-errors)
- [Performance Issues](#performance-issues)
- [Environment Issues](#environment-issues)
- [Getting More Help](#getting-more-help)
- [Prevention Checklist](#prevention-checklist)
---
## Migration Errors
### Error: "url: undefined"
**Symptom:**
```
Error: url is undefined in dbCredentials
```
**Cause:** Environment variables not loaded during migration.
**Solutions:**
**Option 1: Explicit env loading**
```bash
export DATABASE_URL="$(grep DATABASE_URL .env.local | cut -d '=' -f2)" && \
[package-manager] drizzle-kit migrate
```
**Option 2: Update drizzle.config.ts**
```typescript
import { defineConfig } from 'drizzle-kit';
import { config } from 'dotenv';
config({ path: '.env.local' });
export default defineConfig({
schema: './src/db/schema.ts',
out: './src/db/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
```
**Option 3: Use programmatic migration**
```typescript
import { migrate } from 'drizzle-orm/neon-http/migrator';
import { db } from './src/db';
import { config } from 'dotenv';
config({ path: '.env.local' });
await migrate(db, { migrationsFolder: './src/db/migrations' });
```
### Error: "Cannot find migrations folder"
**Symptom:**
```
Error: ENOENT: no such file or directory, scandir './src/db/migrations'
```
**Cause:** Migrations folder doesn't exist yet.
**Solution:**
```bash
mkdir -p src/db/migrations
[package-manager] drizzle-kit generate
```
### Error: "Column already exists"
**Symptom:**
```
Error: column "name" of relation "users" already exists
```
**Cause:** Trying to add a column that already exists in the database.
**Solutions:**
**Option 1: Skip migration (dev only)**
```bash
rm src/db/migrations/[latest-migration-file].sql
[package-manager] drizzle-kit generate
```
**Option 2: Drop and recreate table (dev only, DATA LOSS)**
```bash
psql $DATABASE_URL -c "DROP TABLE users CASCADE;"
[package-manager] drizzle-kit migrate
```
**Option 3: Manual migration (production)**
Edit the migration file to check if column exists:
```sql
ALTER TABLE users
ADD COLUMN IF NOT EXISTS name VARCHAR(255);
```
### Error: "Migration already applied"
**Symptom:**
```
Error: migration has already been applied
```
**Cause:** Drizzle tracks applied migrations. Trying to reapply.
**Solution:**
Check migration journal:
```bash
cat src/db/migrations/meta/_journal.json
```
Remove duplicate entry or regenerate:
```bash
rm -rf src/db/migrations
mkdir src/db/migrations
[package-manager] drizzle-kit generate
```
**Warning:** Only do this in development!
## Connection Errors
### Error: "Connection refused"
**Symptom:**
```
Error: connect ECONNREFUSED
```
**Causes and Solutions:**
**1. Wrong DATABASE_URL format**
Check format:
```bash
echo $DATABASE_URL
```
Should be:
```
postgresql://user:password@host.neon.tech/dbname?sslmode=require
```
**2. Missing sslmode**
Add to DATABASE_URL:
```
?sslmode=require
```
**3. Firewall/network issue**
Test connectivity:
```bash
psql $DATABASE_URL -c "SELECT 1"
```
### Error: "WebSocket connection failed"
**Symptom:**
```
Error: WebSocket connection to 'wss://...' failed
```
**Cause:** Missing WebSocket constructor in Node.js.
**Solution:**
Add to your connection file:
```typescript
import { neonConfig } from '@neondatabase/serverless';
import ws from 'ws';
neonConfig.webSocketConstructor = ws;
```
Install ws if missing:
```bash
[package-manager] add ws
[package-manager] add -D @types/ws
```
### Error: "Too many connections"
**Symptom:**
```
Error: sorry, too many clients already
```
**Cause:** Connection pool exhausted.
**Solutions:**
**For HTTP adapter:** This shouldn't happen (stateless).
**For WebSocket adapter:** Implement connection pooling:
```typescript
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({
connectionString: process.env.DATABASE_URL!,
max: 10, // Limit connections
});
export const db = drizzle(pool);
```
**Close connections properly:**
```typescript
process.on('SIGTERM', async () => {
await pool.end();
process.exit(0);
});
```
## Adapter Issues
### Wrong Adapter for Environment
**Symptom:** App works locally but fails in production (or vice versa).
**Cause:** Using wrong adapter for environment.
**Solutions:**
See `references/adapters.md` for decision guide.
**Quick reference:**
- Vercel/Cloudflare/Edge → HTTP adapter
- Node.js/Express/Long-lived → WebSocket adapter
**HTTP adapter:**
```typescript
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql);
```
**WebSocket adapter:**
```typescript
import { drizzle } from 'drizzle-orm/neon-serverless';
import { Pool, neonConfig } from '@neondatabase/serverless';
import ws from 'ws';
neonConfig.webSocketConstructor = ws;
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
export const db = drizzle(pool);
```
## Type Errors
### Error: "Type 'number' is not assignable to type 'string'"
**Symptom:**
```typescript
const user = await db.insert(users).values({
id: 1, // Error here
email: 'test@example.com',
});
```
**Cause:** Trying to manually set auto-increment ID.
**Solution:**
Remove `id` from insert (it's auto-generated):
```typescript
const user = await db.insert(users).values({
email: 'test@example.com',
});
```
### Error: "Property 'xyz' does not exist"
**Symptom:**
```typescript
const user = await db.select().from(users);
console.log(user[0].nonExistentField); // Error
```
**Cause:** Column not defined in schema.
**Solution:**
Add column to schema:
```typescript
export const users = pgTable('users', {
id: serial('id').primaryKey(),
nonExistentField: text('non_existent_field'),
});
```
Then regenerate and apply migration.
## Query Errors
### Error: "relation does not exist"
**Symptom:**
```
Error: relation "users" does not exist
```
**Cause:** Table not created in database yet.
**Solution:**
Run migrations:
```bash
[package-manager] drizzle-kit generate
export DATABASE_URL="$(grep DATABASE_URL .env.local | cut -d '=' -f2)" && \
[package-manager] drizzle-kit migrate
```
### Error: "column does not exist"
**Symptom:**
```
Error: column "email" does not exist
```
**Causes:**
**1. Schema out of sync with database**
Regenerate and apply migrations:
```bash
[package-manager] drizzle-kit generate
[package-manager] drizzle-kit migrate
```
**2. Wrong table name in query**
Check schema definition vs query.
**3. Case sensitivity**
PostgreSQL is case-sensitive. Ensure column names match exactly.
### Error: "Cannot perform transactions with HTTP adapter"
**Symptom:**
```typescript
await db.transaction(async (tx) => {
// Error: transactions not supported
});
```
**Cause:** HTTP adapter doesn't support transactions.
**Solutions:**
**Option 1: Switch to WebSocket adapter** (if environment allows)
See `references/adapters.md`.
**Option 2: Use batch operations**
```typescript
await db.batch([
db.insert(users).values({ email: 'test1@example.com' }),
db.insert(posts).values({ title: 'Test' }),
]);
```
**Option 3: Implement application-level rollback**
Not ideal, but possible for simple cases.
## Performance Issues
### Slow Queries
**Symptoms:** Queries taking seconds instead of milliseconds.
**Diagnose:**
**1. Missing indexes**
Check if foreign keys have indexes:
```typescript
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
authorId: serial('author_id').notNull(),
}, (table) => ({
authorIdIdx: index('posts_author_id_idx').on(table.authorId), // ADD THIS
}));
```
**2. N+1 queries**
Use relations instead of multiple queries:
```typescript
const postsWithAuthors = await db.query.posts.findMany({
with: {
author: true,
},
});
```
**3. Selecting too much data**
Select only needed columns:
```typescript
const users = await db.select({
id: users.id,
email: users.email,
}).from(users);
```
### Connection Timeout
**Symptom:** Queries timeout in production.
**Solutions:**
**1. For Vercel:** Ensure using HTTP adapter (see `references/adapters.md`)
**2. For Node.js:** Implement connection pooling with retry:
```typescript
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({
connectionString: process.env.DATABASE_URL!,
max: 10,
connectionTimeoutMillis: 5000,
idleTimeoutMillis: 30000,
});
```
**3. Add query timeout:**
```typescript
const result = await Promise.race([
db.select().from(users),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Query timeout')), 5000)
),
]);
```
## Environment Issues
### Error: "DATABASE_URL is undefined"
**Symptom:** App can't find DATABASE_URL.
**Solutions:**
**1. Check env file exists:**
```bash
ls .env .env.local
```
**2. Verify var is set:**
```bash
grep DATABASE_URL .env.local
```
**3. Load env vars:**
```typescript
import { config } from 'dotenv';
config({ path: '.env.local' });
```
**4. For Next.js:** Use `NEXT_PUBLIC_` prefix if accessing client-side (NOT recommended for DATABASE_URL):
```
# Don't do this - security risk
NEXT_PUBLIC_DATABASE_URL="..."
# Do this - server-only
DATABASE_URL="..."
```
### Error: "Invalid connection string"
**Symptom:**
```
Error: invalid connection string
```
**Cause:** Malformed DATABASE_URL.
**Check format:**
```
postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=require
```
**Common mistakes:**
- Missing `postgresql://` prefix
- Special characters in password not URL-encoded
- Missing `?sslmode=require`
**Fix special characters:**
```bash
# If password is "p@ss&word!"
# Encode to: p%40ss%26word%21
```
## Getting More Help
If your issue isn't listed here:
1. **Check adapter configuration:** `references/adapters.md`
2. **Review migration patterns:** `references/migrations.md`
3. **Check query syntax:** `references/query-patterns.md`
4. **Search Drizzle docs:** https://orm.drizzle.team/docs
5. **Check Neon docs:** https://neon.com/docs
## Prevention Checklist
Before deploying:
- [ ] Environment variables properly loaded
- [ ] Correct adapter for environment
- [ ] Migrations applied successfully
- [ ] Indexes on foreign keys
- [ ] Connection pooling configured (if Node.js)
- [ ] Error handling for database operations
- [ ] .env files in .gitignore
- [ ] Test queries work in production environment