Troubleshooting
Start with the first failing boundary: process startup, PostgreSQL, authentication, platform email, organization invoice email, or a feature layer. Capture the deployed commit, timestamp, affected organization and role, request path, and the earliest relevant server error before changing configuration.
The container exits before serving traffic
The repository entrypoint runs drizzle-kit migrate before the Nuxt server. Any migration error exits the container deliberately.
- Read the migration output immediately before
Migration failed!. - Confirm
DATABASE_URLreaches the intended database from the release environment. - Confirm the database user can create and alter the required schemas, tables, indexes, and constraints.
- Confirm the image and
drizzle/history come from the same source commit. - Compare the recorded migration journal with the expected history.
- Reproduce against a restored copy before attempting a production repair.
Do not delete migration journal rows, drop schemas, or repeatedly restart a destructive migration without understanding its state. Prefer a reviewed forward repair. See database migrations and upgrade recovery.
The application cannot use PostgreSQL
Check the complete connection path: DNS, network policy, TLS requirements, credentials, database name, connection limits, and database availability. A successful homepage request is not proof of a healthy database-backed session.
If failures appear only under load, inspect PostgreSQL connection counts, CPU, memory, storage, locks, and slow queries. If they began after a release, compare the target commit's schema and query changes with the previous pin.
GitHub or Google sign-in fails
Provider buttons are controlled by public feature flags, while the server registers a provider only when both its flag and credentials are present. If credentials are absent, explicitly set the corresponding flag to false; otherwise a button can be visible without a working server provider.
Verify all of the following:
BETTER_AUTH_URLorPUBLIC_URLis the externally visible HTTPS origin;- the provider application uses that exact origin and scheme;
- GitHub's callback is
/api/auth/callback/github; - Google's callback is
/api/auth/callback/google; - the client ID and secret belong to the same provider application;
- proxy headers preserve the public host and protocol;
- the origin is trusted when a custom proxy or additional frontend is involved.
Better Auth documents the callback paths and provider setup for GitHub and Google. For GitHub accounts with a private email address, also verify the OAuth application's email permission as described in the GitHub provider guide.
OTP, invitation, or recovery email does not arrive
Platform authentication email requires both RESEND_API_KEY and RESEND_FROM_EMAIL. When either prerequisite is missing, the current email utility logs a warning and skips delivery; the initiating UI flow may still appear to continue.
- Search server logs for
Email prerequisites not met,RESEND_API_KEY, orRESEND_FROM_EMAIL. - Verify the sender domain and address in Resend.
- Inspect the provider's delivery, suppression, bounce, and spam information.
- Check that
PUBLIC_URLandBETTER_AUTH_URLproduce reachable links. - Retest with a controlled recipient and correlate the timestamp with server and provider logs.
Do not log or paste OTPs, reset links, session cookies, or API keys into a public issue.
Invoice email cannot be configured or sent
Invoice email uses an organization-specific Resend credential configured by an organization owner under /settings/organization; it does not use the platform authentication email key.
- The API key must be allowed to list Resend domains. A send-only restricted key is rejected because Customer Portal validates domain ownership and sending status.
- The selected sender domain must be verified and enabled for sending. Use Check again after changing provider state; ordinary results are cached briefly.
- The invoice sender address must use a verified domain.
- A single attachment may be at most 10 MB, and all email attachments together may be at most 40 MB.
- Inspect the invoice's email history. Customer Portal records
PENDING,SENT, orFAILED, the provider message ID, and the latest checked provider status.
Use the invoice action to refresh delivery status when Resend has accepted a message but final delivery is unclear. See create and manage invoices for the user workflow.
A feature layer does not appear
Customer Portal discovers directories immediately below layers/ that contain nuxt.config.ts.
- Confirm the directory is not nested one level too deep.
- Confirm its
nuxt.config.tsloads without an import error. - Restart the development server after adding, removing, or renaming the layer.
- Check the feature definition, audience and policy registration, navigation placement, and required locale keys.
- Confirm its schema files match the root Drizzle glob and its migrations are present.
- Test with the correct active organization and role; authorized navigation can be intentionally hidden.
Continue with feature layers or the feature contract reference.