Troubleshooting
Clean-machine setup failures and their exact fixes
Google sign-in fails with redirect_uri_mismatch
Google rejects the OAuth round-trip when the redirect URI is not registered character-for-character. Two lists must both be right:
- In the Google Cloud Console, the OAuth client must list the Supabase callback:
https://<project-ref>.supabase.co/auth/v1/callback. - In the Supabase dashboard, the allowed redirect list must contain the app callback for your host —
http://localhost:3000/auth/callbacklocally, your deployed origin (for examplehttps://demo.buildgrain.com/auth/callback) in production.
Trailing slashes, http vs https, and port numbers all count. After editing either list, retry in a fresh browser session — Google caches consent state.
The login screen shows a setup blocker instead of a Google button
This is intentional, not a crash. When SUPABASE_URL, SUPABASE_ANON_KEY, or the Google OAuth env vars are missing, the kit shows an explicit blocker naming the missing keys (packages/auth/src/mode.ts) — it never fakes a login. An invalid APP_MODE value blocks the same way rather than silently falling back.
Fix: set the keys listed on the blocker screen in your env file and restart pnpm dev. The full key matrix per mode lives in the env matrix module and renders in the admin Developer surface.
Queries fail behind the connection pooler (prepared statements)
Transaction-mode poolers (Supabase pgbouncer on port 6543) reject prepared statements, and the postgres-js default is to use them. The kit already ships prepare: false in packages/db/src/client.ts — if you fork the db client or swap drivers, keep that setting or every query fails once you point DATABASE_URL at the pooler.
Run migrations (pnpm db:setup) against the direct connection string (port 5432); keep the runtime on the pooler URL. See ADR-003 in /docs/concepts for the pool sizing rationale.
It works on PGlite locally but behaves differently on Postgres
Without a DATABASE_URL, the kit boots an in-memory PGlite database so tests and offline development run with zero infrastructure. PGlite is not byte-for-byte Postgres: there is no real network latency, no concurrent connection limit, and no pooler in the path, and server extensions are unavailable.
Before deploying, run the app at least once against a real Postgres (local Docker or Supabase) with your production-shaped DATABASE_URL. Never point a demo or production-kit deployment at PGlite — the db client treats it as a local/test fallback only.
Port 3000 is already in use
pnpm dev binds port 3000; a stale dev server or another app blocks it.
lsof -ti :3000 | xargs killThe e2e suite starts its own Next servers in one process tree and isolates their compiler output via NEXT_DIST_DIR (see apps/web/next.config.ts), so a running dev server can also collide with pnpm e2e — stop dev before running the suite.
I added myself to ADMIN_ALLOWLIST_EMAILS but I am not an admin
The allowlist syncs to User.systemAdmin on sign-in, not live. Sign out and sign back in; removal downgrades on the following sign-in the same way. There is deliberately no UI toggle for system admin — the allowlist is the only mechanism (see /docs/setup).
I cannot delete the seed accounts
northwind-labs, beacon-health, and corvus-analytics are protected: deletion is blocked at the API layer and the nightly reset restores them to their seed state. This keeps the public sample stable for every visitor. Create your own workspace for destructive experiments — visitor workspaces are removed after 24 hours of inactivity on the demo.
A generated-content drift test fails after I edited source files
Some committed modules in packages/content are generated from live sources and drift-tested against them — the Code tab samples (pnpm sync:code-samples) and the stack manifest (pnpm sync:stack-manifest). If you edit a sampled file or change dependencies, the corresponding test fails on purpose. Rerun the sync script it names and commit the regenerated module.