Troubleshooting
Redirects point at localhost:3000
Section titled “Redirects point at localhost:3000”Symptom. After deploying to a server or domain, login redirects and internal links go to
http://localhost:3000 instead of your domain.
Cause. NEXT_PUBLIC_WEBAPP_URL (and optionally NEXTAUTH_URL) still hold the default value.
Fix.
NEXT_PUBLIC_WEBAPP_URL=https://cal.example.comNEXTAUTH_URL=https://cal.example.com- No trailing slash.
NEXTAUTH_URLis optional whenNEXT_PUBLIC_WEBAPP_URLis set: NextAuth infers the base URL from the request’sHostheader.NEXT_PUBLIC_WEBAPP_URLis inlined by Next.js at build time. For a self-built image, rebuild after changing it. The published image rewrites the placeholder on first container start, so setting the run-time variable is enough there.- On Vercel,
NEXTAUTH_URLis inferred fromVERCEL_URLand does not need to be set.
CLIENT_FETCH_ERROR in the logs
Section titled “CLIENT_FETCH_ERROR in the logs”Symptom.
[next-auth][error][CLIENT_FETCH_ERROR]request to http://<your-domain>/api/auth/session failed, reason: getaddrinfo ENOTFOUNDCause. The container cannot resolve its own public hostname from inside the Docker network.
Fix. Point the hostname at the container itself:
services: flexcal: extra_hosts: - "cal.example.com:127.0.0.1"This keeps NEXTAUTH_URL on the public URL, which OAuth callbacks require.
Setting NEXTAUTH_URL=http://localhost:3000/api/auth also silences the error, but breaks OAuth:
Google and Microsoft would redirect users to localhost.
TLS errors behind a reverse proxy
Section titled “TLS errors behind a reverse proxy”Symptom. Requests fail with certificate errors when flexcal sits behind a load balancer that terminates HTTPS.
Option 1 — proxy forwards plain HTTP internally (most common). Keep NEXTAUTH_URL on the public
HTTPS URL and forward the standard headers:
proxy_set_header Host $host;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-Forwarded-Host $host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;Option 2 — internal self-signed certificates. Trust your internal CA:
NODE_EXTRA_CA_CERTS=/path/to/internal-ca.crtOption 3 — last resort.
NODE_TLS_REJECT_UNAUTHORIZED=0First user setup fails
Section titled “First user setup fails”Cause. Usually migrations have not been applied, or a previous setup attempt left a partial row.
Fix.
-
Apply migrations:
Terminal window bun run --cwd packages/prisma db-deploy -
Confirm
DATABASE_URLis correct and the database is reachable. -
Check the application logs for the Prisma error, which names the failing field or constraint.
The /auth/setup endpoint creates the first user with the ADMIN role and only runs while the
User table is empty. Clear the table before retrying if an earlier attempt half-succeeded.
Invalid prisma.user.create() with empty metadata
Section titled “Invalid prisma.user.create() with empty metadata”Some versions cannot create a user when metadata is empty. Use an empty JSON object {} instead.
id auto-increments, so leave it blank.
No key set vapidDetails.publicKey
Section titled “No key set vapidDetails.publicKey”Web push is not configured. Generate a key pair and set both halves:
npx web-push generate-vapid-keysNEXT_PUBLIC_VAPID_PUBLIC_KEY=<public>VAPID_PRIVATE_KEY=<private>Prisma: The database schema is not empty (P3005)
Section titled “Prisma: The database schema is not empty (P3005)”Prisma tracks applied migrations in a _prisma_migrations table. If that table disagrees with the
database, it refuses to continue. Mark the already-applied migrations as resolved, one at a time:
bun run prisma migrate resolve --applied <migration_name>See Database migrations for the development-side details.
Getting further help
Section titled “Getting further help”Search the issue tracker first, then open a new issue
with your deployment method, the relevant .env keys (values redacted) and the log output.