Debug production issues on Vercel using logs, database inspection, and proper deployment waiting
IMPORTANT: Always use --scope prologe for the 0 Finance project:
# All Vercel commands need --scope prologe
vercel logs www.0.finance --scope prologe
vercel ls --scope prologe
vercel env ls --scope prologe
# Stream live logs (waits for new logs)
vercel logs www.0.finance --scope prologe
# Logs since a specific time
vercel logs www.0.finance --scope prologe --since 5m
vercel logs www.0.finance --scope prologe --since 1h
# Filter logs (pipe to grep)
vercel logs www.0.finance --scope prologe 2>&1 | grep -i "error"
vercel logs www.0.finance --scope prologe 2>&1 | grep "ai-email"
Note: vercel logs can be slow/hang. Use timeout or Ctrl+C if needed:
timeout 30 vercel logs www.0.finance --scope prologe --since 5m 2>&1 | head -100
# List recent deployments
vercel ls --scope prologe | head -10
# Check specific deployment status
vercel inspect <deployment-url> --scope prologe
# Get latest deployment URL
vercel ls --scope prologe 2>/dev/null | head -1
DO NOT just sleep and hope. Use vercel inspect --wait:
# Get the latest deployment URL
LATEST=$(vercel ls --scope prologe 2>/dev/null | head -1)
# Wait for it to be ready (up to 5 minutes)
vercel inspect "$LATEST" --scope prologe --wait --timeout 5m
# Check the status in the output:
# status: ● Building -> still building
# status: ● Ready -> deployed and live!
# status: ● Error -> build failed
Full workflow after pushing code:
# 1. Push your changes
git push origin main
# 2. Wait a few seconds for Vercel to pick it up
sleep 5
# 3. Get the new deployment URL
LATEST=$(vercel ls --scope prologe 2>/dev/null | head -1)
echo "Waiting for: $LATEST"
# 4. Wait for it to complete
vercel inspect "$LATEST" --scope prologe --wait --timeout 5m
# 5. Now your changes are live!
Check deployment details:
# See build info, aliases, and status
vercel inspect <deployment-url> --scope prologe
# See build logs if something failed
vercel inspect <deployment-url> --scope prologe --logs
# Redeploy production from current state
vercel --prod --scope prologe
# Or push to git and wait
git push origin main
# Then use the waiting method above
# List env vars
vercel env ls --scope prologe
# Add an env var (will prompt for value)
echo "value" | vercel env add VAR_NAME production --scope prologe
# Pull env vars to local .env
vercel env pull .env.local --scope prologe
CRITICAL: Always load .env.production.local for production database:
import * as dotenv from 'dotenv';
import path from 'path';
// Load production env
dotenv.config({
path: path.resolve(__dirname, '../.env.production.local'),
});
Or in a script:
cd /path/to/zerofinance/packages/web
pnpm tsx -e "
import * as dotenv from 'dotenv';
dotenv.config({ path: '.env.production.local' });
// Now use db...
import { db } from './src/db';
"
Zero Finance uses Neon Postgres:
| Environment | Host Pattern | Used By |
|---|---|---|
| Development | ep-aged-cherry-* |
Local dev, some scripts |
| Production | ep-wispy-recipe-* or similar |
Vercel deployment, prod data |
Always verify which database you're connecting to:
[DB] Connecting to database host: ep-xxxxx-pooler.us-east-1.aws.neon.tech
| Table | Purpose |
|---|---|
user_safes |
Safe addresses linked to users/workspaces |
incoming_deposits |
Incoming USDC transfers (synced from Safe API) |
outgoing_transfers |
Outgoing transactions from Safes |
users |
User records with primaryWorkspaceId |
workspace_members |
User-workspace membership |
earn_deposits |
Vault deposit records |
ai_email_sessions |
AI email agent conversation sessions |
import * as dotenv from 'dotenv';
dotenv.config({ path: '.env.production.local' });
import { db } from './src/db';
import { userSafes, incomingDeposits } from './src/db/schema';
import { eq } from 'drizzle-orm';
async function main() {
// Your debugging code here
const safes = await db.select().from(userSafes);
console.log('Total safes:', safes.length);
}
main()
.then(() => process.exit(0))
.catch(console.error);
Debug steps:
curl -s -X POST "https://www.0.finance/api/endpoint" \
-H "Content-Type: application/json" \
-d '{"test": true}'
Causes:
Fix:
vercel ls --scope prologe | head -3vercel --prod --scope prologeDebug:
const safe = await db.query.userSafes.findFirst({
where: eq(userSafes.safeAddress, '0x...'),
});
const user = await db.query.users.findFirst({
where: eq(users.privyDid, safe.userDid),
});
console.log('Safe workspace:', safe.workspaceId);
console.log('User primary workspace:', user.primaryWorkspaceId);
# Check it's set in Vercel
vercel env ls --scope prologe | grep VAR_NAME
# Check which environments it's set for (production, preview, development)
# May need to redeploy for changes to take effect
This skill is part of the testing pyramid. Use it when:
| Scenario | Skill to Load |
|---|---|
| Testing on staging first | test-staging-branch |
| Making code testable | testability |
| After debugging, capture learnings | skill-reinforcement |
1. Check Vercel logs first (fastest)
2. If unclear, inspect production DB
3. If still unclear, reproduce locally
4. After fix, test on staging before prod
5. Update this skill with new patterns
Append new discoveries here
Symptom: POSTGRES_URL missing or wrong host while using .env.production.local.
Root Cause: packages/web/src/db/index.ts loads .env.local on import, which can run before dotenv config.
Fix: In one-off scripts, call dotenv.config({ path: '.env.production.local' }) first and dynamically import ./src/db afterward.
Prevention: Avoid static imports of ./src/db in CLI scripts; use dynamic import after dotenv setup.
### YYYY-MM-DD: [Issue Description]
**Symptom**: [What you saw]
**Root Cause**: [Why it happened]
**Fix**: [How to resolve]
**Prevention**: [How to avoid in future]