Troubleshooting
Common issues running or deploying the marketing website, and how to fix them.
The API won't start
"Invalid environment configuration" on boot - a required env value is
missing or malformed. The error lists the offending fields. Check
website/backend/.env against configuration.md. Common
causes: a malformed DATABASE_URL, or a non-numeric PORT / JWT_TTL_HOURS.
Port already in use - another process holds port 4000. Stop it or set a
different PORT.
/api/health/db returns { "ok": false }
The API is up but cannot reach PostgreSQL.
- Confirm PostgreSQL is running and reachable at the host/port in
DATABASE_URL. - Confirm the role/password and database name exist (see installation.md).
- For managed Postgres, you likely need TLS - append
?sslmode=require(or?sslmode=no-verifyfor self-signed certs) toDATABASE_URL.
Migrations
Tables are missing - run npm run migrate in website/backend. Pending
migrations also run automatically on startup, so check the boot logs for
"Applied migration" / "No pending migrations".
A migration failed - it runs inside a transaction and rolls back on error,
so a failure leaves the schema unchanged and is not recorded in
schema_migrations. Fix the cause (often connectivity or privileges) and run
again.
Forms fail in the browser
CORS error in the console - the API rejects the frontend's origin. Add the
frontend origin to CORS_ORIGINS (comma-separated) and restart the API. For
production this must be your exact https://leadfella.com origin.
"Network error - please check your connection" - the frontend can't reach the
API. Confirm the API is running and that NEXT_PUBLIC_API_URL matches its URL.
Remember NEXT_PUBLIC_* is baked in at build time - rebuild after changing
it.
HTTP 429 "Too many requests" - the submit rate limit (5/minute per IP)
fired. This is expected protection; wait a minute, or raise
RATE_LIMIT_*/the submit limiter for load testing.
A submission "succeeds" but nothing is stored - the hidden honeypot field was filled, so the request was treated as a bot and silently accepted. Real form submissions leave the honeypot empty.
Admin panel
Login fails with valid-looking credentials - verify ADMIN_EMAIL matches
what you typed and that either ADMIN_PASSWORD_HASH or ADMIN_PASSWORD is set.
With neither set, login is disabled by design (the API logs an error). Regenerate
a hash with npm run seed:admin -- "<password>".
Signed out unexpectedly / repeated logins - the admin token expired
(JWT_TTL_HOURS, default 12h) or JWT_SECRET changed (rotating it invalidates
all sessions). Sign in again.
Can't reach /admin in production - it is intentionally noindex but still
served; confirm the frontend deployment includes it and the API origin is in
CORS_ORIGINS.
Build issues
Frontend npm run build type errors - run npm run typecheck for the full
list. The build fails on type or lint errors by design.
Frontend calls the wrong API in production - NEXT_PUBLIC_API_URL was unset
or wrong at build time. Set it and rebuild.
Maintenance scripts
A lead script hits the wrong database - the scripts read DATABASE_URL; if
unset they fall back to the dev default (.../leadfella_site). Export the real
DATABASE_URL before running, e.g. list-leads, export-leads, delete-lead.
Where do exports go? - export-leads writes to website/backend/exports/
(gitignored, since CSVs may contain PII) unless you pass --out or --stdout.
Still stuck? Check the backend logs (pino, console + structured) and the browser console/network tab, then see configuration.md.