AUTH STARTER · BUGS WE ALREADY HIT
Bugs we already hit
Real production gotchas, with the exact symptom and the exact fix. Grows with each release.
OAuth — redirect_uri_mismatch
Callback URL on the provider doesn't match what Better Auth sends. Almost always a trailing slash or http/https mismatch. Compare character by character: https://yourdomain.com/api/auth/callback/google (no trailing slash, https, no www unless your domain uses www).
Magic link emails in spam
DKIM, not the email content. Verify the sender domain in Resend, paste the DKIM and SPF records into DNS, wait 10 minutes, send another test. If it's still in spam, check DMARC — sometimes Gmail rejects with no DMARC policy.
GitHub OAuth returns null email
GitHub only returns email if you ask for user:email scope. Check authServerConfig.socialProviders.github.scopes includes "user:email". If a user has their primary email private on GitHub, even with the scope, you'll get null. Better Auth handles it — the user picks one during sign-up.
Passkey registration silently fails
Three usual causes: (1) you're on http (use HTTPS or localhost), (2) the rpID doesn't match the current hostname, (3) the user agent doesn't support WebAuthn. Open DevTools → Console → look for a WebAuthn-related warning.
Session is null after sign-in
Usually one of: (1) nextCookies() isn't last in the plugins array, (2) cookies are being blocked (third-party cookies in iframes), (3) BETTER_AUTH_URL doesn't match the actual origin (cookies set for example.com don't appear on yourdomain.com).
2FA codes always invalid
Time skew. The server clock is off. ntp date the server, or move to a managed runtime where clocks are synced for you (Vercel, Fly, Render).
"Email already exists" with a fresh email
The email column has a unique constraint and Better Auth normalizes to lowercase. If a previous sign-up failed mid-flow, a row may already exist. Drizzle Studio → user table → delete the orphan row.
Drizzle migration drift
Never hand-edit a generated migration that's already been applied somewhere. If a migration is wrong, generate a new one that reverses + redoes. Drift between local and prod has cost more late nights than any other single thing.
If you hit something not on this list
Ping us — we'd rather update this page once than answer the same DM five times. The fix you save is the next person's evening.