docs / troubleshooting

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:

  1. In the Google Cloud Console, the OAuth client must list the Supabase callback: https://<project-ref>.supabase.co/auth/v1/callback.
  2. In the Supabase dashboard, the allowed redirect list must contain the app callback for your host — http://localhost:3000/auth/callback locally, your deployed origin (for example https://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 NEXT_PUBLIC_SUPABASE_URL or NEXT_PUBLIC_SUPABASE_ANON_KEY is missing, the kit shows an explicit blocker naming the missing keys (packages/auth/src/mode.ts) — it never fakes a login. Google provider credentials stay in the Supabase dashboard and are not separate app env vars. 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.

To verify rollback without touching Postgres, run NODE_ENV=test APP_MODE=test pnpm db:test:reset with DATABASE_URL unset. The command rejects non-test modes, external database URLs, and persistent PGlite directories. It performs the rollback and clean-reset probes only in disposable in-memory PGlite databases; it is not a production rollback tool.

Port 3000 is already in use

pnpm dev binds port 3000; a stale dev server or another app blocks it.

lsof -ti :3000 | xargs kill

The 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

sample-co, atlas-pilot, and northstar-trial are protected: deletion is blocked at the API layer. The current repository does not automatically restore those records or remove visitor workspaces. Use an operator-reviewed process for cleanup, and do not put real or sensitive data in a sample workspace.

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.